codemirror-languageserver 1.18.0 → 1.19.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 +76 -3
- package/dist/formatting.d.ts +6 -0
- package/dist/highlight.d.ts +2 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.js +550 -67
- package/dist/jsonrpc.d.ts +34 -0
- package/dist/plugin.d.ts +36 -6
- package/dist/rename.d.ts +4 -0
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -14,6 +14,9 @@ https://user-images.githubusercontent.com/348107/120141150-c6bb9180-c1fd-11eb-8a
|
|
|
14
14
|
- 📚 Hover Documentation
|
|
15
15
|
- 🩺 Diagnostics
|
|
16
16
|
- 🔍 Go to Definition, Declaration, and Type Definition
|
|
17
|
+
- 🔦 Document Highlight
|
|
18
|
+
- 🎨 Document Formatting and Range Formatting
|
|
19
|
+
- ✏️ Rename Symbol
|
|
17
20
|
|
|
18
21
|
## Usage
|
|
19
22
|
|
|
@@ -24,8 +27,6 @@ npm i codemirror-languageserver
|
|
|
24
27
|
```js
|
|
25
28
|
import { languageServer } from 'codemirror-languageserver';
|
|
26
29
|
|
|
27
|
-
const transport = new WebSocketTransport(serverUri);
|
|
28
|
-
|
|
29
30
|
var ls = languageServer({
|
|
30
31
|
// WebSocket server uri and other client options.
|
|
31
32
|
serverUri,
|
|
@@ -33,7 +34,7 @@ var ls = languageServer({
|
|
|
33
34
|
|
|
34
35
|
// Alternatively, to share the same client across multiple instances of this plugin.
|
|
35
36
|
client: new LanguageServerClient({
|
|
36
|
-
serverUri,
|
|
37
|
+
transport: new WebSocketTransport(serverUri),
|
|
37
38
|
rootUri: 'file:///',
|
|
38
39
|
}),
|
|
39
40
|
|
|
@@ -52,6 +53,78 @@ var view = new EditorView({
|
|
|
52
53
|
});
|
|
53
54
|
```
|
|
54
55
|
|
|
56
|
+
### Custom Transport
|
|
57
|
+
|
|
58
|
+
Use `languageServerWithTransport` to provide a custom transport (e.g. for non-WebSocket connections):
|
|
59
|
+
|
|
60
|
+
```js
|
|
61
|
+
import { languageServerWithTransport } from 'codemirror-languageserver';
|
|
62
|
+
|
|
63
|
+
var ls = languageServerWithTransport({
|
|
64
|
+
transport: myCustomTransport, // Must implement the Transport interface
|
|
65
|
+
rootUri: 'file:///',
|
|
66
|
+
documentUri: `file:///${filename}`,
|
|
67
|
+
languageId: 'cpp',
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The `Transport` interface requires `send`, `onMessage`, `onClose`, `onError`, and `close` methods. You can import `Transport` and `WebSocketTransport` from the package.
|
|
72
|
+
|
|
73
|
+
### Document Highlight
|
|
74
|
+
|
|
75
|
+
Document highlights are enabled by default. When the cursor is on a symbol, all occurrences are highlighted. Style them with CSS:
|
|
76
|
+
|
|
77
|
+
```css
|
|
78
|
+
.cm-lsp-highlight-text,
|
|
79
|
+
.cm-lsp-highlight-read { background-color: rgba(255, 255, 0, 0.2); }
|
|
80
|
+
.cm-lsp-highlight-write { background-color: rgba(255, 165, 0, 0.3); }
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Formatting
|
|
84
|
+
|
|
85
|
+
`formatDocument` and `formatSelection` are CodeMirror commands that you can bind to keys:
|
|
86
|
+
|
|
87
|
+
```js
|
|
88
|
+
import { keymap } from '@codemirror/view';
|
|
89
|
+
import { formatDocument, formatSelection } from 'codemirror-languageserver';
|
|
90
|
+
|
|
91
|
+
keymap.of([
|
|
92
|
+
{ key: 'Shift-Alt-f', run: formatDocument },
|
|
93
|
+
{ key: 'Ctrl-k Ctrl-f', run: formatSelection },
|
|
94
|
+
])
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
To configure formatting options (tab size, spaces vs tabs, etc.), use the `formattingOptions` facet:
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
import { formattingOptions } from 'codemirror-languageserver';
|
|
101
|
+
|
|
102
|
+
// In extensions:
|
|
103
|
+
formattingOptions.of({
|
|
104
|
+
tabSize: 2,
|
|
105
|
+
insertSpaces: true,
|
|
106
|
+
trimTrailingWhitespace: true,
|
|
107
|
+
insertFinalNewline: true,
|
|
108
|
+
})
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
By default, `tabSize` is read from the editor state.
|
|
112
|
+
|
|
113
|
+
### Rename Symbol
|
|
114
|
+
|
|
115
|
+
`renameSymbol` is a CodeMirror command that opens a rename prompt at the top of the editor:
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
import { keymap } from '@codemirror/view';
|
|
119
|
+
import { renameSymbol } from 'codemirror-languageserver';
|
|
120
|
+
|
|
121
|
+
keymap.of([
|
|
122
|
+
{ key: 'F2', run: renameSymbol },
|
|
123
|
+
])
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Style the rename panel with CSS using the `cm-lsp-rename-panel` and `cm-lsp-rename-input` classes.
|
|
127
|
+
|
|
55
128
|
### Using with Initialization Options
|
|
56
129
|
|
|
57
130
|
The plugin includes built-in TypeScript definitions for popular language servers:
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Facet } from '@codemirror/state';
|
|
2
|
+
import type { EditorView } from '@codemirror/view';
|
|
3
|
+
import type * as LSP from 'vscode-languageserver-protocol';
|
|
4
|
+
export declare const formattingOptions: Facet<LSP.FormattingOptions, LSP.FormattingOptions>;
|
|
5
|
+
export declare function formatDocument(view: EditorView): boolean;
|
|
6
|
+
export declare function formatSelection(view: EditorView): boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export { LanguageServerClient, languageServerPlugin, SynchronizationMethod, } from './plugin';
|
|
2
|
+
export { WebSocketTransport } from './jsonrpc';
|
|
3
|
+
export type { Transport } from './jsonrpc';
|
|
2
4
|
export { jumpToDefinition, jumpToDefinitionPos, jumpToDefinitionKeymap, } from './definition';
|
|
5
|
+
export { formatDocument, formatSelection, formattingOptions, } from './formatting';
|
|
6
|
+
export { renameSymbol } from './rename';
|
|
3
7
|
export { PyrightInitializationOptions, RustAnalyzerInitializationOptions, TypeScriptInitializationOptions, ESLintInitializationOptions, ClangdInitializationOptions, GoplsInitializationOptions, } from './initialization';
|
|
4
8
|
import { LanguageServerClient, SynchronizationMethod } from './plugin';
|
|
5
9
|
import type { LanguageServerClientOptions, LanguageServerBaseOptions } from './plugin';
|
|
@@ -12,5 +16,5 @@ interface LanguageServerWebsocketOptions<InitializationOptions = unknown> extend
|
|
|
12
16
|
serverUri: `ws://${string}` | `wss://${string}`;
|
|
13
17
|
initializationOptions?: InitializationOptions;
|
|
14
18
|
}
|
|
15
|
-
export declare function languageServer<InitializationOptions = unknown>(options: LanguageServerWebsocketOptions<InitializationOptions>): import("@codemirror/state").Extension[];
|
|
16
|
-
export declare function languageServerWithTransport<InitializationOptions = unknown>(options: LanguageServerOptions<InitializationOptions>): import("@codemirror/state").Extension[];
|
|
19
|
+
export declare function languageServer<InitializationOptions = unknown>(options: LanguageServerWebsocketOptions<InitializationOptions>): (import("@codemirror/state").Extension | import("@codemirror/state").StateField<string>)[];
|
|
20
|
+
export declare function languageServerWithTransport<InitializationOptions = unknown>(options: LanguageServerOptions<InitializationOptions>): (import("@codemirror/state").Extension | import("@codemirror/state").StateField<string>)[];
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,120 @@
|
|
|
1
|
-
import { insertCompletionText, autocompletion as autocompletion$1 } from '@codemirror/autocomplete';
|
|
2
1
|
import { setDiagnostics } from '@codemirror/lint';
|
|
3
|
-
import { Facet } from '@codemirror/state';
|
|
4
|
-
import { ViewPlugin, hoverTooltip as hoverTooltip$1, EditorView, keymap } from '@codemirror/view';
|
|
5
|
-
import {
|
|
6
|
-
import { CompletionItemKind, DiagnosticSeverity, CompletionTriggerKind } from 'vscode-languageserver-protocol';
|
|
2
|
+
import { Facet, StateEffect, StateField } from '@codemirror/state';
|
|
3
|
+
import { ViewPlugin, showPanel, hoverTooltip as hoverTooltip$1, EditorView, Decoration, keymap } from '@codemirror/view';
|
|
4
|
+
import { CompletionItemKind, DiagnosticSeverity, CompletionTriggerKind, DocumentHighlightKind } from 'vscode-languageserver-protocol';
|
|
7
5
|
import { marked } from 'marked';
|
|
6
|
+
import { autocompletion as autocompletion$1 } from '@codemirror/autocomplete';
|
|
7
|
+
|
|
8
|
+
class WebSocketTransport {
|
|
9
|
+
constructor(uri) {
|
|
10
|
+
this.connection = new WebSocket(uri);
|
|
11
|
+
}
|
|
12
|
+
send(message) {
|
|
13
|
+
this.connection.send(message);
|
|
14
|
+
}
|
|
15
|
+
onMessage(callback) {
|
|
16
|
+
this.connection.addEventListener('message', (event) => {
|
|
17
|
+
callback(typeof event.data === 'string' ? event.data : '');
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
onClose(callback) {
|
|
21
|
+
this.connection.addEventListener('close', callback);
|
|
22
|
+
}
|
|
23
|
+
onError(callback) {
|
|
24
|
+
this.connection.addEventListener('error', () => {
|
|
25
|
+
callback(new Error('WebSocket error'));
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
close() {
|
|
29
|
+
this.connection.close();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
class JSONRPCClient {
|
|
33
|
+
constructor(transport) {
|
|
34
|
+
this.nextId = 0;
|
|
35
|
+
this.pending = new Map();
|
|
36
|
+
this.notificationHandlers = [];
|
|
37
|
+
this.transport = transport;
|
|
38
|
+
this.ready = new Promise((resolve, reject) => {
|
|
39
|
+
if (transport instanceof WebSocketTransport) {
|
|
40
|
+
const ws = transport.connection;
|
|
41
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
42
|
+
resolve();
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
ws.addEventListener('open', () => resolve());
|
|
46
|
+
ws.addEventListener('error', () => reject(new Error('WebSocket connection failed')));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
resolve();
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
transport.onMessage((raw) => {
|
|
54
|
+
let msg;
|
|
55
|
+
try {
|
|
56
|
+
msg = JSON.parse(raw);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if ('id' in msg && msg.id != null && this.pending.has(msg.id)) {
|
|
62
|
+
const pending = this.pending.get(msg.id);
|
|
63
|
+
this.pending.delete(msg.id);
|
|
64
|
+
clearTimeout(pending.timer);
|
|
65
|
+
if (msg.error) {
|
|
66
|
+
pending.reject(new Error(msg.error.message || 'JSON-RPC error'));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
pending.resolve(msg.result);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else if ('method' in msg) {
|
|
73
|
+
for (const handler of this.notificationHandlers) {
|
|
74
|
+
handler(msg);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
async request(req, timeout) {
|
|
80
|
+
await this.ready;
|
|
81
|
+
const id = this.nextId++;
|
|
82
|
+
const message = {
|
|
83
|
+
jsonrpc: '2.0',
|
|
84
|
+
id,
|
|
85
|
+
method: req.method,
|
|
86
|
+
params: req.params,
|
|
87
|
+
};
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
const timer = setTimeout(() => {
|
|
90
|
+
this.pending.delete(id);
|
|
91
|
+
reject(new Error(`Request ${req.method} timed out`));
|
|
92
|
+
}, timeout);
|
|
93
|
+
this.pending.set(id, { resolve, reject, timer });
|
|
94
|
+
this.transport.send(JSON.stringify(message));
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
async notify(notification) {
|
|
98
|
+
await this.ready;
|
|
99
|
+
const message = {
|
|
100
|
+
jsonrpc: '2.0',
|
|
101
|
+
method: notification.method,
|
|
102
|
+
params: notification.params,
|
|
103
|
+
};
|
|
104
|
+
this.transport.send(JSON.stringify(message));
|
|
105
|
+
}
|
|
106
|
+
onNotification(handler) {
|
|
107
|
+
this.notificationHandlers.push(handler);
|
|
108
|
+
}
|
|
109
|
+
close() {
|
|
110
|
+
for (const [id, pending] of this.pending) {
|
|
111
|
+
clearTimeout(pending.timer);
|
|
112
|
+
pending.reject(new Error('Client closed'));
|
|
113
|
+
}
|
|
114
|
+
this.pending.clear();
|
|
115
|
+
this.transport.close();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
8
118
|
|
|
9
119
|
function posToOffset(doc, pos) {
|
|
10
120
|
if (pos.line >= doc.lines) {
|
|
@@ -46,6 +156,7 @@ function changeSetToEvents(doc, changes) {
|
|
|
46
156
|
}
|
|
47
157
|
|
|
48
158
|
const timeout = 10000;
|
|
159
|
+
const changesDelay = 500;
|
|
49
160
|
const CompletionItemKindMap = Object.fromEntries(Object.entries(CompletionItemKind).map(([key, value]) => [value, key]));
|
|
50
161
|
const useLast = (values) => values.reduce((_, v) => v, '');
|
|
51
162
|
Facet.define({
|
|
@@ -61,26 +172,19 @@ class LanguageServerClient {
|
|
|
61
172
|
this.autoClose = options.autoClose;
|
|
62
173
|
this.plugins = [];
|
|
63
174
|
this.transport = options.transport;
|
|
64
|
-
this.
|
|
65
|
-
this.client = new Client(this.requestManager);
|
|
175
|
+
this.client = new JSONRPCClient(this.transport);
|
|
66
176
|
this.client.onNotification((data) => {
|
|
177
|
+
if (data.method && data.id) {
|
|
178
|
+
// Server-to-client request: respond with null result.
|
|
179
|
+
// https://github.com/FurqanSoftware/codemirror-languageserver/issues/9
|
|
180
|
+
this.transport.send(JSON.stringify({
|
|
181
|
+
jsonrpc: '2.0',
|
|
182
|
+
id: data.id,
|
|
183
|
+
result: null,
|
|
184
|
+
}));
|
|
185
|
+
}
|
|
67
186
|
this.processNotification(data);
|
|
68
187
|
});
|
|
69
|
-
const webSocketTransport = this.transport;
|
|
70
|
-
if (webSocketTransport && webSocketTransport.connection) {
|
|
71
|
-
// XXX(hjr265): Need a better way to do this. Relevant issue:
|
|
72
|
-
// https://github.com/FurqanSoftware/codemirror-languageserver/issues/9
|
|
73
|
-
webSocketTransport.connection.addEventListener('message', (message) => {
|
|
74
|
-
const data = JSON.parse(message.data);
|
|
75
|
-
if (data.method && data.id) {
|
|
76
|
-
webSocketTransport.connection.send(JSON.stringify({
|
|
77
|
-
jsonrpc: '2.0',
|
|
78
|
-
id: data.id,
|
|
79
|
-
result: null,
|
|
80
|
-
}));
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
188
|
this.initializePromise = this.initialize();
|
|
85
189
|
}
|
|
86
190
|
getInitializeParams() {
|
|
@@ -135,6 +239,19 @@ class LanguageServerClient {
|
|
|
135
239
|
dynamicRegistration: true,
|
|
136
240
|
linkSupport: true,
|
|
137
241
|
},
|
|
242
|
+
documentHighlight: {
|
|
243
|
+
dynamicRegistration: true,
|
|
244
|
+
},
|
|
245
|
+
formatting: {
|
|
246
|
+
dynamicRegistration: true,
|
|
247
|
+
},
|
|
248
|
+
rangeFormatting: {
|
|
249
|
+
dynamicRegistration: true,
|
|
250
|
+
},
|
|
251
|
+
rename: {
|
|
252
|
+
dynamicRegistration: true,
|
|
253
|
+
prepareSupport: true,
|
|
254
|
+
},
|
|
138
255
|
},
|
|
139
256
|
workspace: {
|
|
140
257
|
didChangeConfiguration: {
|
|
@@ -143,6 +260,7 @@ class LanguageServerClient {
|
|
|
143
260
|
},
|
|
144
261
|
},
|
|
145
262
|
initializationOptions: (_a = this.options.initializationOptions) !== null && _a !== void 0 ? _a : null,
|
|
263
|
+
locale: this.options.locale,
|
|
146
264
|
processId: null,
|
|
147
265
|
rootUri: this.rootUri,
|
|
148
266
|
workspaceFolders: this.workspaceFolders,
|
|
@@ -178,6 +296,21 @@ class LanguageServerClient {
|
|
|
178
296
|
async textDocumentTypeDefinition(params) {
|
|
179
297
|
return await this.request('textDocument/typeDefinition', params, timeout);
|
|
180
298
|
}
|
|
299
|
+
async textDocumentDocumentHighlight(params) {
|
|
300
|
+
return await this.request('textDocument/documentHighlight', params, timeout);
|
|
301
|
+
}
|
|
302
|
+
async textDocumentFormatting(params) {
|
|
303
|
+
return await this.request('textDocument/formatting', params, timeout);
|
|
304
|
+
}
|
|
305
|
+
async textDocumentRangeFormatting(params) {
|
|
306
|
+
return await this.request('textDocument/rangeFormatting', params, timeout);
|
|
307
|
+
}
|
|
308
|
+
async textDocumentPrepareRename(params) {
|
|
309
|
+
return await this.request('textDocument/prepareRename', params, timeout);
|
|
310
|
+
}
|
|
311
|
+
async textDocumentRename(params) {
|
|
312
|
+
return await this.request('textDocument/rename', params, timeout);
|
|
313
|
+
}
|
|
181
314
|
async completionItemResolve(params) {
|
|
182
315
|
return await this.request('completionItem/resolve', params, timeout);
|
|
183
316
|
}
|
|
@@ -214,11 +347,14 @@ var SynchronizationMethod;
|
|
|
214
347
|
class LanguageServerPlugin {
|
|
215
348
|
constructor(view, { client, documentUri, languageId, allowHTMLContent, synchronizationMethod, }) {
|
|
216
349
|
this.view = view;
|
|
350
|
+
this.changesTimeout = null;
|
|
351
|
+
this.pendingChanges = null;
|
|
217
352
|
this.client = client;
|
|
218
353
|
this.documentUri = documentUri;
|
|
219
354
|
this.languageId = languageId;
|
|
220
355
|
this.allowHTMLContent = allowHTMLContent;
|
|
221
|
-
this.synchronizationMethod =
|
|
356
|
+
this.synchronizationMethod =
|
|
357
|
+
synchronizationMethod !== null && synchronizationMethod !== void 0 ? synchronizationMethod : SynchronizationMethod.Full;
|
|
222
358
|
this.documentVersion = 0;
|
|
223
359
|
this.client.attachPlugin(this);
|
|
224
360
|
this.initialize({
|
|
@@ -229,20 +365,40 @@ class LanguageServerPlugin {
|
|
|
229
365
|
if (!docChanged) {
|
|
230
366
|
return;
|
|
231
367
|
}
|
|
368
|
+
if (this.changesTimeout) {
|
|
369
|
+
clearTimeout(this.changesTimeout);
|
|
370
|
+
}
|
|
232
371
|
switch (this.synchronizationMethod) {
|
|
233
372
|
case SynchronizationMethod.Full:
|
|
234
|
-
this.
|
|
373
|
+
this.pendingChanges = [
|
|
235
374
|
{
|
|
236
375
|
text: this.view.state.doc.toString(),
|
|
237
376
|
},
|
|
238
|
-
]
|
|
377
|
+
];
|
|
239
378
|
break;
|
|
240
379
|
case SynchronizationMethod.Incremental:
|
|
241
|
-
this.
|
|
380
|
+
if (!this.pendingChanges) {
|
|
381
|
+
this.pendingChanges = [];
|
|
382
|
+
}
|
|
383
|
+
this.pendingChanges.push(...changeSetToEvents(startState.doc, changes));
|
|
242
384
|
break;
|
|
243
385
|
}
|
|
386
|
+
this.changesTimeout = setTimeout(() => {
|
|
387
|
+
this.changesTimeout = null;
|
|
388
|
+
if (this.pendingChanges) {
|
|
389
|
+
this.sendChanges(this.pendingChanges);
|
|
390
|
+
this.pendingChanges = null;
|
|
391
|
+
}
|
|
392
|
+
}, changesDelay);
|
|
244
393
|
}
|
|
245
394
|
destroy() {
|
|
395
|
+
if (this.changesTimeout) {
|
|
396
|
+
clearTimeout(this.changesTimeout);
|
|
397
|
+
if (this.pendingChanges) {
|
|
398
|
+
this.sendChanges(this.pendingChanges);
|
|
399
|
+
this.pendingChanges = null;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
246
402
|
this.client.detachPlugin(this);
|
|
247
403
|
}
|
|
248
404
|
async initialize({ documentText }) {
|
|
@@ -301,11 +457,12 @@ class LanguageServerPlugin {
|
|
|
301
457
|
}
|
|
302
458
|
const dom = document.createElement('div');
|
|
303
459
|
dom.classList.add('documentation');
|
|
304
|
-
|
|
305
|
-
|
|
460
|
+
const formatted = await formatContents(contents);
|
|
461
|
+
if (formatted.isHTML || this.allowHTMLContent) {
|
|
462
|
+
dom.innerHTML = formatted.value;
|
|
306
463
|
}
|
|
307
464
|
else {
|
|
308
|
-
dom.textContent =
|
|
465
|
+
dom.textContent = formatted.value;
|
|
309
466
|
}
|
|
310
467
|
return {
|
|
311
468
|
pos,
|
|
@@ -365,56 +522,52 @@ class LanguageServerPlugin {
|
|
|
365
522
|
label,
|
|
366
523
|
detail,
|
|
367
524
|
apply(view, completion, from, to) {
|
|
525
|
+
const changes = [];
|
|
368
526
|
if (isLSPTextEdit(textEdit)) {
|
|
369
|
-
|
|
527
|
+
changes.push({
|
|
528
|
+
from: posToOffset(view.state.doc, textEdit.range.start),
|
|
529
|
+
to: Math.max(posToOffset(view.state.doc, textEdit.range.end), to),
|
|
530
|
+
insert: textEdit.newText,
|
|
531
|
+
});
|
|
370
532
|
}
|
|
371
533
|
else {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
534
|
+
changes.push({
|
|
535
|
+
from,
|
|
536
|
+
to,
|
|
537
|
+
insert: label,
|
|
538
|
+
});
|
|
376
539
|
}
|
|
377
|
-
additionalTextEdits
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
540
|
+
if (additionalTextEdits) {
|
|
541
|
+
for (const edit of additionalTextEdits) {
|
|
542
|
+
changes.push({
|
|
543
|
+
from: posToOffset(view.state.doc, edit.range.start),
|
|
544
|
+
to: posToOffset(view.state.doc, edit.range.end),
|
|
545
|
+
insert: edit.newText,
|
|
546
|
+
});
|
|
382
547
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
return -1;
|
|
386
|
-
}
|
|
387
|
-
return 0;
|
|
388
|
-
})
|
|
389
|
-
.forEach((textEdit) => {
|
|
390
|
-
view.dispatch(view.state.update({
|
|
391
|
-
changes: {
|
|
392
|
-
from: posToOffset(view.state.doc, textEdit.range.start),
|
|
393
|
-
to: posToOffset(view.state.doc, textEdit.range.end),
|
|
394
|
-
insert: textEdit.newText,
|
|
395
|
-
},
|
|
396
|
-
}));
|
|
397
|
-
});
|
|
548
|
+
}
|
|
549
|
+
view.dispatch(view.state.update({ changes }));
|
|
398
550
|
},
|
|
399
551
|
type: kind && CompletionItemKindMap[kind].toLowerCase(),
|
|
400
552
|
};
|
|
401
553
|
completion.info = async () => {
|
|
402
554
|
var _a;
|
|
403
|
-
|
|
404
|
-
if (!documentation &&
|
|
555
|
+
if (!item.documentation &&
|
|
405
556
|
((_a = this.client.capabilities.completionProvider) === null || _a === void 0 ? void 0 : _a.resolveProvider)) {
|
|
406
557
|
const resolvedItem = await this.client.completionItemResolve(item);
|
|
407
|
-
documentation = resolvedItem.documentation;
|
|
558
|
+
item.documentation = resolvedItem.documentation;
|
|
408
559
|
}
|
|
560
|
+
const { documentation } = item;
|
|
409
561
|
if (!documentation)
|
|
410
562
|
return null;
|
|
411
563
|
const dom = document.createElement('div');
|
|
412
564
|
dom.classList.add('documentation');
|
|
413
|
-
|
|
414
|
-
|
|
565
|
+
const formatted = await formatContents(documentation);
|
|
566
|
+
if (formatted.isHTML || this.allowHTMLContent) {
|
|
567
|
+
dom.innerHTML = formatted.value;
|
|
415
568
|
}
|
|
416
569
|
else {
|
|
417
|
-
dom.textContent =
|
|
570
|
+
dom.textContent = formatted.value;
|
|
418
571
|
}
|
|
419
572
|
return dom;
|
|
420
573
|
};
|
|
@@ -510,19 +663,32 @@ class LanguageServerPlugin {
|
|
|
510
663
|
const languageServerPlugin = ViewPlugin.fromClass(LanguageServerPlugin);
|
|
511
664
|
async function formatContents(contents) {
|
|
512
665
|
if (isLSPMarkupContent(contents)) {
|
|
513
|
-
let value = contents.value;
|
|
514
666
|
if (contents.kind === 'markdown') {
|
|
515
|
-
|
|
667
|
+
return {
|
|
668
|
+
value: await marked.parse(contents.value),
|
|
669
|
+
isHTML: true,
|
|
670
|
+
};
|
|
516
671
|
}
|
|
517
|
-
return value;
|
|
672
|
+
return { value: contents.value, isHTML: false };
|
|
518
673
|
}
|
|
519
674
|
else if (Array.isArray(contents)) {
|
|
520
|
-
|
|
675
|
+
const parts = await Promise.all(contents.map((c) => formatContents(c)));
|
|
676
|
+
const isHTML = parts.some((p) => p.isHTML);
|
|
677
|
+
const value = parts
|
|
678
|
+
.map((p) => (isHTML && !p.isHTML ? escapeHTML(p.value) : p.value))
|
|
679
|
+
.join(isHTML ? '<br><br>' : '\n\n');
|
|
680
|
+
return { value, isHTML };
|
|
521
681
|
}
|
|
522
682
|
else if (typeof contents === 'string') {
|
|
523
|
-
return contents;
|
|
683
|
+
return { value: contents, isHTML: false };
|
|
524
684
|
}
|
|
525
685
|
}
|
|
686
|
+
function escapeHTML(text) {
|
|
687
|
+
return text
|
|
688
|
+
.replace(/&/g, '&')
|
|
689
|
+
.replace(/</g, '<')
|
|
690
|
+
.replace(/>/g, '>');
|
|
691
|
+
}
|
|
526
692
|
function toSet(chars) {
|
|
527
693
|
let preamble = '';
|
|
528
694
|
let flat = Array.from(chars).join('');
|
|
@@ -578,6 +744,223 @@ const jumpToDefinitionKeymap = [
|
|
|
578
744
|
{ key: 'Mod-F12', run: jumpToTypeDefinition, preventDefault: true },
|
|
579
745
|
];
|
|
580
746
|
|
|
747
|
+
const formattingOptions = Facet.define({
|
|
748
|
+
combine(values) {
|
|
749
|
+
return Object.assign({ tabSize: 4, insertSpaces: true }, ...values);
|
|
750
|
+
},
|
|
751
|
+
});
|
|
752
|
+
function getFormattingOptions(view) {
|
|
753
|
+
var _a;
|
|
754
|
+
const opts = view.state.facet(formattingOptions);
|
|
755
|
+
return {
|
|
756
|
+
...opts,
|
|
757
|
+
tabSize: (_a = opts.tabSize) !== null && _a !== void 0 ? _a : view.state.tabSize,
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
function applyTextEdits(view, edits) {
|
|
761
|
+
const changes = edits.map((edit) => ({
|
|
762
|
+
from: posToOffset(view.state.doc, edit.range.start),
|
|
763
|
+
to: posToOffset(view.state.doc, edit.range.end),
|
|
764
|
+
insert: edit.newText,
|
|
765
|
+
}));
|
|
766
|
+
view.dispatch(view.state.update({ changes }));
|
|
767
|
+
}
|
|
768
|
+
function formatDocument(view) {
|
|
769
|
+
var _a;
|
|
770
|
+
const plugin = view.plugin(languageServerPlugin);
|
|
771
|
+
if (!plugin)
|
|
772
|
+
return false;
|
|
773
|
+
if (!plugin.client.ready ||
|
|
774
|
+
!((_a = plugin.client.capabilities) === null || _a === void 0 ? void 0 : _a.documentFormattingProvider)) {
|
|
775
|
+
return false;
|
|
776
|
+
}
|
|
777
|
+
plugin.client
|
|
778
|
+
.textDocumentFormatting({
|
|
779
|
+
textDocument: { uri: plugin.documentUri },
|
|
780
|
+
options: getFormattingOptions(view),
|
|
781
|
+
})
|
|
782
|
+
.then((edits) => {
|
|
783
|
+
if (edits && edits.length > 0) {
|
|
784
|
+
applyTextEdits(view, edits);
|
|
785
|
+
}
|
|
786
|
+
});
|
|
787
|
+
return true;
|
|
788
|
+
}
|
|
789
|
+
function formatSelection(view) {
|
|
790
|
+
var _a;
|
|
791
|
+
const plugin = view.plugin(languageServerPlugin);
|
|
792
|
+
if (!plugin)
|
|
793
|
+
return false;
|
|
794
|
+
if (!plugin.client.ready ||
|
|
795
|
+
!((_a = plugin.client.capabilities) === null || _a === void 0 ? void 0 : _a.documentRangeFormattingProvider)) {
|
|
796
|
+
return false;
|
|
797
|
+
}
|
|
798
|
+
const { from, to } = view.state.selection.main;
|
|
799
|
+
plugin.client
|
|
800
|
+
.textDocumentRangeFormatting({
|
|
801
|
+
textDocument: { uri: plugin.documentUri },
|
|
802
|
+
range: {
|
|
803
|
+
start: offsetToPos(view.state.doc, from),
|
|
804
|
+
end: offsetToPos(view.state.doc, to),
|
|
805
|
+
},
|
|
806
|
+
options: getFormattingOptions(view),
|
|
807
|
+
})
|
|
808
|
+
.then((edits) => {
|
|
809
|
+
if (edits && edits.length > 0) {
|
|
810
|
+
applyTextEdits(view, edits);
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
return true;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
const openRenamePanel = StateEffect.define();
|
|
817
|
+
const closeRenamePanel = StateEffect.define();
|
|
818
|
+
const renamePanelState = StateField.define({
|
|
819
|
+
create() {
|
|
820
|
+
return null;
|
|
821
|
+
},
|
|
822
|
+
update(value, tr) {
|
|
823
|
+
for (const effect of tr.effects) {
|
|
824
|
+
if (effect.is(openRenamePanel))
|
|
825
|
+
return effect.value;
|
|
826
|
+
if (effect.is(closeRenamePanel))
|
|
827
|
+
return null;
|
|
828
|
+
}
|
|
829
|
+
return value;
|
|
830
|
+
},
|
|
831
|
+
provide: (field) => showPanel.from(field, (value) => value != null ? (view) => createRenamePanel(view, value) : null),
|
|
832
|
+
});
|
|
833
|
+
function applyWorkspaceEdit(view, edit) {
|
|
834
|
+
const plugin = view.plugin(languageServerPlugin);
|
|
835
|
+
if (!plugin)
|
|
836
|
+
return;
|
|
837
|
+
const changes = [];
|
|
838
|
+
if (edit.changes) {
|
|
839
|
+
const edits = edit.changes[plugin.documentUri];
|
|
840
|
+
if (edits) {
|
|
841
|
+
for (const e of edits) {
|
|
842
|
+
changes.push({
|
|
843
|
+
from: posToOffset(view.state.doc, e.range.start),
|
|
844
|
+
to: posToOffset(view.state.doc, e.range.end),
|
|
845
|
+
insert: e.newText,
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
if (edit.documentChanges) {
|
|
851
|
+
for (const change of edit.documentChanges) {
|
|
852
|
+
if ('textDocument' in change) {
|
|
853
|
+
if (change.textDocument.uri !== plugin.documentUri)
|
|
854
|
+
continue;
|
|
855
|
+
for (const e of change.edits) {
|
|
856
|
+
if ('range' in e) {
|
|
857
|
+
changes.push({
|
|
858
|
+
from: posToOffset(view.state.doc, e.range.start),
|
|
859
|
+
to: posToOffset(view.state.doc, e.range.end),
|
|
860
|
+
insert: e.newText,
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
if (changes.length > 0) {
|
|
868
|
+
view.dispatch(view.state.update({ changes }));
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
function createRenamePanel(view, placeholder) {
|
|
872
|
+
const dom = document.createElement('div');
|
|
873
|
+
dom.className = 'cm-lsp-rename-panel';
|
|
874
|
+
const input = document.createElement('input');
|
|
875
|
+
input.className = 'cm-lsp-rename-input';
|
|
876
|
+
input.value = placeholder;
|
|
877
|
+
input.setAttribute('aria-label', 'New name');
|
|
878
|
+
const submit = () => {
|
|
879
|
+
const newName = input.value.trim();
|
|
880
|
+
if (!newName || newName === placeholder) {
|
|
881
|
+
view.dispatch({ effects: closeRenamePanel.of(null) });
|
|
882
|
+
view.focus();
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
performRename(view, newName);
|
|
886
|
+
};
|
|
887
|
+
input.addEventListener('keydown', (e) => {
|
|
888
|
+
if (e.key === 'Enter') {
|
|
889
|
+
e.preventDefault();
|
|
890
|
+
submit();
|
|
891
|
+
}
|
|
892
|
+
else if (e.key === 'Escape') {
|
|
893
|
+
e.preventDefault();
|
|
894
|
+
view.dispatch({ effects: closeRenamePanel.of(null) });
|
|
895
|
+
view.focus();
|
|
896
|
+
}
|
|
897
|
+
});
|
|
898
|
+
dom.appendChild(input);
|
|
899
|
+
return {
|
|
900
|
+
dom,
|
|
901
|
+
top: true,
|
|
902
|
+
mount() {
|
|
903
|
+
input.select();
|
|
904
|
+
},
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
async function performRename(view, newName) {
|
|
908
|
+
const plugin = view.plugin(languageServerPlugin);
|
|
909
|
+
if (!plugin)
|
|
910
|
+
return;
|
|
911
|
+
const pos = view.state.selection.main.head;
|
|
912
|
+
view.dispatch({ effects: closeRenamePanel.of(null) });
|
|
913
|
+
view.focus();
|
|
914
|
+
const result = await plugin.client.textDocumentRename({
|
|
915
|
+
textDocument: { uri: plugin.documentUri },
|
|
916
|
+
position: offsetToPos(view.state.doc, pos),
|
|
917
|
+
newName,
|
|
918
|
+
});
|
|
919
|
+
if (result) {
|
|
920
|
+
applyWorkspaceEdit(view, result);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
function renameSymbol(view) {
|
|
924
|
+
var _a;
|
|
925
|
+
const plugin = view.plugin(languageServerPlugin);
|
|
926
|
+
if (!plugin)
|
|
927
|
+
return false;
|
|
928
|
+
if (!plugin.client.ready ||
|
|
929
|
+
!((_a = plugin.client.capabilities) === null || _a === void 0 ? void 0 : _a.renameProvider)) {
|
|
930
|
+
return false;
|
|
931
|
+
}
|
|
932
|
+
const pos = view.state.selection.main.head;
|
|
933
|
+
const capabilities = plugin.client.capabilities;
|
|
934
|
+
const renameProvider = capabilities.renameProvider;
|
|
935
|
+
const supportsPrepare = typeof renameProvider === 'object' && renameProvider.prepareProvider;
|
|
936
|
+
if (supportsPrepare) {
|
|
937
|
+
plugin.client
|
|
938
|
+
.textDocumentPrepareRename({
|
|
939
|
+
textDocument: { uri: plugin.documentUri },
|
|
940
|
+
position: offsetToPos(view.state.doc, pos),
|
|
941
|
+
})
|
|
942
|
+
.then((result) => {
|
|
943
|
+
if (!result)
|
|
944
|
+
return;
|
|
945
|
+
const placeholder = 'placeholder' in result
|
|
946
|
+
? result.placeholder
|
|
947
|
+
: view.state.doc.sliceString(posToOffset(view.state.doc, result.start), posToOffset(view.state.doc, result.end));
|
|
948
|
+
view.dispatch({
|
|
949
|
+
effects: openRenamePanel.of(placeholder),
|
|
950
|
+
});
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
else {
|
|
954
|
+
const word = view.state.wordAt(pos);
|
|
955
|
+
const placeholder = word
|
|
956
|
+
? view.state.doc.sliceString(word.from, word.to)
|
|
957
|
+
: '';
|
|
958
|
+
view.dispatch({ effects: openRenamePanel.of(placeholder) });
|
|
959
|
+
}
|
|
960
|
+
return true;
|
|
961
|
+
}
|
|
962
|
+
const renameExtension = () => renamePanelState;
|
|
963
|
+
|
|
581
964
|
const hoverTooltip = () => hoverTooltip$1((view, pos) => {
|
|
582
965
|
var _a;
|
|
583
966
|
const plugin = view.plugin(languageServerPlugin);
|
|
@@ -600,7 +983,8 @@ const autocompletion = () => autocompletion$1({
|
|
|
600
983
|
trigKind = CompletionTriggerKind.TriggerCharacter;
|
|
601
984
|
trigChar = line.text[pos - line.from - 1];
|
|
602
985
|
}
|
|
603
|
-
if (
|
|
986
|
+
if (!explicit &&
|
|
987
|
+
trigKind === CompletionTriggerKind.Invoked &&
|
|
604
988
|
!context.matchBefore(/\w+$/)) {
|
|
605
989
|
return null;
|
|
606
990
|
}
|
|
@@ -612,6 +996,103 @@ const autocompletion = () => autocompletion$1({
|
|
|
612
996
|
],
|
|
613
997
|
});
|
|
614
998
|
|
|
999
|
+
const highlightField = StateField.define({
|
|
1000
|
+
create() {
|
|
1001
|
+
return Decoration.none;
|
|
1002
|
+
},
|
|
1003
|
+
update(decorations, tr) {
|
|
1004
|
+
for (const effect of tr.effects) {
|
|
1005
|
+
if (effect.is(setHighlightsEffect)) {
|
|
1006
|
+
return effect.value;
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
if (tr.docChanged) {
|
|
1010
|
+
return Decoration.none;
|
|
1011
|
+
}
|
|
1012
|
+
return decorations;
|
|
1013
|
+
},
|
|
1014
|
+
provide: (field) => EditorView.decorations.from(field),
|
|
1015
|
+
});
|
|
1016
|
+
const setHighlightsEffect = StateEffect.define();
|
|
1017
|
+
const textDecoration = Decoration.mark({
|
|
1018
|
+
class: 'cm-lsp-highlight-text',
|
|
1019
|
+
});
|
|
1020
|
+
const readDecoration = Decoration.mark({
|
|
1021
|
+
class: 'cm-lsp-highlight-read',
|
|
1022
|
+
});
|
|
1023
|
+
const writeDecoration = Decoration.mark({
|
|
1024
|
+
class: 'cm-lsp-highlight-write',
|
|
1025
|
+
});
|
|
1026
|
+
function decorationForKind(kind) {
|
|
1027
|
+
switch (kind) {
|
|
1028
|
+
case DocumentHighlightKind.Read:
|
|
1029
|
+
return readDecoration;
|
|
1030
|
+
case DocumentHighlightKind.Write:
|
|
1031
|
+
return writeDecoration;
|
|
1032
|
+
default:
|
|
1033
|
+
return textDecoration;
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
const highlightPlugin = ViewPlugin.fromClass(class {
|
|
1037
|
+
constructor() {
|
|
1038
|
+
this.pending = -1;
|
|
1039
|
+
}
|
|
1040
|
+
update(update) {
|
|
1041
|
+
if (!update.selectionSet && !update.docChanged) {
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
if (update.docChanged) {
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
clearTimeout(this.pending);
|
|
1048
|
+
this.pending = setTimeout(() => this.requestHighlights(update.view), 100);
|
|
1049
|
+
}
|
|
1050
|
+
async requestHighlights(view) {
|
|
1051
|
+
var _a;
|
|
1052
|
+
const plugin = view.plugin(languageServerPlugin);
|
|
1053
|
+
if (!plugin)
|
|
1054
|
+
return;
|
|
1055
|
+
const { state } = view;
|
|
1056
|
+
const pos = state.selection.main.head;
|
|
1057
|
+
if (!plugin.client.ready ||
|
|
1058
|
+
!((_a = plugin.client.capabilities) === null || _a === void 0 ? void 0 : _a.documentHighlightProvider)) {
|
|
1059
|
+
view.dispatch({ effects: setHighlightsEffect.of(Decoration.none) });
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1062
|
+
const result = await plugin.client.textDocumentDocumentHighlight({
|
|
1063
|
+
textDocument: { uri: plugin.documentUri },
|
|
1064
|
+
position: offsetToPos(state.doc, pos),
|
|
1065
|
+
});
|
|
1066
|
+
if (view.state.selection.main.head !== pos) {
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
if (!result || result.length === 0) {
|
|
1070
|
+
view.dispatch({ effects: setHighlightsEffect.of(Decoration.none) });
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
const decorations = result
|
|
1074
|
+
.map((highlight) => {
|
|
1075
|
+
const from = posToOffset(view.state.doc, highlight.range.start);
|
|
1076
|
+
const to = posToOffset(view.state.doc, highlight.range.end);
|
|
1077
|
+
if (from == null || to == null)
|
|
1078
|
+
return null;
|
|
1079
|
+
return decorationForKind(highlight.kind).range(from, to);
|
|
1080
|
+
})
|
|
1081
|
+
.filter((d) => d != null)
|
|
1082
|
+
.sort((a, b) => a.from - b.from);
|
|
1083
|
+
view.dispatch({
|
|
1084
|
+
effects: setHighlightsEffect.of(Decoration.set(decorations)),
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
destroy() {
|
|
1088
|
+
clearTimeout(this.pending);
|
|
1089
|
+
}
|
|
1090
|
+
});
|
|
1091
|
+
const documentHighlight = () => [
|
|
1092
|
+
highlightField,
|
|
1093
|
+
highlightPlugin,
|
|
1094
|
+
];
|
|
1095
|
+
|
|
615
1096
|
const mouseHandler = () => EditorView.domEventHandlers({
|
|
616
1097
|
mousedown: (event, view) => {
|
|
617
1098
|
if (!event.ctrlKey && !event.metaKey)
|
|
@@ -651,9 +1132,11 @@ function languageServerWithTransport(options) {
|
|
|
651
1132
|
}),
|
|
652
1133
|
hoverTooltip(),
|
|
653
1134
|
autocompletion(),
|
|
1135
|
+
documentHighlight(),
|
|
1136
|
+
renameExtension(),
|
|
654
1137
|
keymap.of([...jumpToDefinitionKeymap]),
|
|
655
1138
|
mouseHandler(),
|
|
656
1139
|
];
|
|
657
1140
|
}
|
|
658
1141
|
|
|
659
|
-
export { LanguageServerClient, SynchronizationMethod, jumpToDefinition, jumpToDefinitionKeymap, jumpToDefinitionPos, languageServer, languageServerPlugin, languageServerWithTransport };
|
|
1142
|
+
export { LanguageServerClient, SynchronizationMethod, WebSocketTransport, formatDocument, formatSelection, formattingOptions, jumpToDefinition, jumpToDefinitionKeymap, jumpToDefinitionPos, languageServer, languageServerPlugin, languageServerWithTransport, renameSymbol };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface Transport {
|
|
2
|
+
send(message: string): void;
|
|
3
|
+
onMessage(callback: (message: string) => void): void;
|
|
4
|
+
onClose(callback: () => void): void;
|
|
5
|
+
onError(callback: (error: Error) => void): void;
|
|
6
|
+
close(): void;
|
|
7
|
+
}
|
|
8
|
+
export declare class WebSocketTransport implements Transport {
|
|
9
|
+
connection: WebSocket;
|
|
10
|
+
constructor(uri: string);
|
|
11
|
+
send(message: string): void;
|
|
12
|
+
onMessage(callback: (message: string) => void): void;
|
|
13
|
+
onClose(callback: () => void): void;
|
|
14
|
+
onError(callback: (error: Error) => void): void;
|
|
15
|
+
close(): void;
|
|
16
|
+
}
|
|
17
|
+
export declare class JSONRPCClient {
|
|
18
|
+
private transport;
|
|
19
|
+
private nextId;
|
|
20
|
+
private pending;
|
|
21
|
+
private notificationHandlers;
|
|
22
|
+
private ready;
|
|
23
|
+
constructor(transport: Transport);
|
|
24
|
+
request(req: {
|
|
25
|
+
method: string;
|
|
26
|
+
params: any;
|
|
27
|
+
}, timeout: number): Promise<any>;
|
|
28
|
+
notify(notification: {
|
|
29
|
+
method: string;
|
|
30
|
+
params: any;
|
|
31
|
+
}): Promise<void>;
|
|
32
|
+
onNotification(handler: (data: any) => void): void;
|
|
33
|
+
close(): void;
|
|
34
|
+
}
|
package/dist/plugin.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { EditorView, Tooltip, ViewPlugin } from '@codemirror/view';
|
|
2
|
+
import type { Transport } from './jsonrpc';
|
|
2
3
|
import { CompletionTriggerKind } from 'vscode-languageserver-protocol';
|
|
3
4
|
import type { CompletionContext, CompletionResult } from '@codemirror/autocomplete';
|
|
4
5
|
import type { PluginValue, ViewUpdate } from '@codemirror/view';
|
|
5
|
-
import { Transport } from '@open-rpc/client-js/build/transports/Transport';
|
|
6
6
|
import type { PublishDiagnosticsParams } from 'vscode-languageserver-protocol';
|
|
7
7
|
import type * as LSP from 'vscode-languageserver-protocol';
|
|
8
8
|
interface LSPRequestMap {
|
|
@@ -24,6 +24,26 @@ interface LSPRequestMap {
|
|
|
24
24
|
LSP.TypeDefinitionParams,
|
|
25
25
|
LSP.Location | LSP.Location[] | LSP.LocationLink[] | null
|
|
26
26
|
];
|
|
27
|
+
'textDocument/documentHighlight': [
|
|
28
|
+
LSP.DocumentHighlightParams,
|
|
29
|
+
LSP.DocumentHighlight[] | null
|
|
30
|
+
];
|
|
31
|
+
'textDocument/formatting': [
|
|
32
|
+
LSP.DocumentFormattingParams,
|
|
33
|
+
LSP.TextEdit[] | null
|
|
34
|
+
];
|
|
35
|
+
'textDocument/rangeFormatting': [
|
|
36
|
+
LSP.DocumentRangeFormattingParams,
|
|
37
|
+
LSP.TextEdit[] | null
|
|
38
|
+
];
|
|
39
|
+
'textDocument/prepareRename': [
|
|
40
|
+
LSP.PrepareRenameParams,
|
|
41
|
+
LSP.Range | {
|
|
42
|
+
range: LSP.Range;
|
|
43
|
+
placeholder: string;
|
|
44
|
+
} | null
|
|
45
|
+
];
|
|
46
|
+
'textDocument/rename': [LSP.RenameParams, LSP.WorkspaceEdit | null];
|
|
27
47
|
'completionItem/resolve': [LSP.CompletionItem, LSP.CompletionItem];
|
|
28
48
|
}
|
|
29
49
|
interface LSPNotifyMap {
|
|
@@ -55,7 +75,6 @@ export declare class LanguageServerClient<InitializationOptions = unknown> {
|
|
|
55
75
|
private workspaceFolders;
|
|
56
76
|
private autoClose?;
|
|
57
77
|
private transport;
|
|
58
|
-
private requestManager;
|
|
59
78
|
private client;
|
|
60
79
|
private plugins;
|
|
61
80
|
private options;
|
|
@@ -63,18 +82,26 @@ export declare class LanguageServerClient<InitializationOptions = unknown> {
|
|
|
63
82
|
protected getInitializeParams(): LSP.InitializeParams;
|
|
64
83
|
initialize(): Promise<void>;
|
|
65
84
|
close(): void;
|
|
66
|
-
textDocumentDidOpen(params: LSP.DidOpenTextDocumentParams): Promise<
|
|
67
|
-
textDocumentDidChange(params: LSP.DidChangeTextDocumentParams): Promise<
|
|
85
|
+
textDocumentDidOpen(params: LSP.DidOpenTextDocumentParams): Promise<void>;
|
|
86
|
+
textDocumentDidChange(params: LSP.DidChangeTextDocumentParams): Promise<void>;
|
|
68
87
|
textDocumentHover(params: LSP.HoverParams): Promise<LSP.Hover>;
|
|
69
88
|
textDocumentCompletion(params: LSP.CompletionParams): Promise<LSP.CompletionList | LSP.CompletionItem[]>;
|
|
70
89
|
textDocumentDefinition(params: LSP.DefinitionParams): Promise<LSP.Location | LSP.Location[] | LSP.LocationLink[]>;
|
|
71
90
|
textDocumentDeclaration(params: LSP.DeclarationParams): Promise<LSP.Location | LSP.Location[] | LSP.LocationLink[]>;
|
|
72
91
|
textDocumentTypeDefinition(params: LSP.TypeDefinitionParams): Promise<LSP.Location | LSP.Location[] | LSP.LocationLink[]>;
|
|
92
|
+
textDocumentDocumentHighlight(params: LSP.DocumentHighlightParams): Promise<LSP.DocumentHighlight[]>;
|
|
93
|
+
textDocumentFormatting(params: LSP.DocumentFormattingParams): Promise<LSP.TextEdit[]>;
|
|
94
|
+
textDocumentRangeFormatting(params: LSP.DocumentRangeFormattingParams): Promise<LSP.TextEdit[]>;
|
|
95
|
+
textDocumentPrepareRename(params: LSP.PrepareRenameParams): Promise<LSP.Range | {
|
|
96
|
+
range: LSP.Range;
|
|
97
|
+
placeholder: string;
|
|
98
|
+
}>;
|
|
99
|
+
textDocumentRename(params: LSP.RenameParams): Promise<LSP.WorkspaceEdit>;
|
|
73
100
|
completionItemResolve(params: LSP.CompletionItem): Promise<LSP.CompletionItem>;
|
|
74
101
|
attachPlugin(plugin: LanguageServerPlugin): void;
|
|
75
102
|
detachPlugin(plugin: LanguageServerPlugin): void;
|
|
76
103
|
protected request<K extends keyof LSPRequestMap>(method: K, params: LSPRequestMap[K][0], timeout: number): Promise<LSPRequestMap[K][1]>;
|
|
77
|
-
protected notify<K extends keyof LSPNotifyMap>(method: K, params: LSPNotifyMap[K]): Promise<
|
|
104
|
+
protected notify<K extends keyof LSPNotifyMap>(method: K, params: LSPNotifyMap[K]): Promise<void>;
|
|
78
105
|
protected processNotification(notification: Notification): void;
|
|
79
106
|
}
|
|
80
107
|
export declare enum SynchronizationMethod {
|
|
@@ -84,11 +111,13 @@ export declare enum SynchronizationMethod {
|
|
|
84
111
|
export declare class LanguageServerPlugin implements PluginValue {
|
|
85
112
|
private view;
|
|
86
113
|
client: LanguageServerClient;
|
|
87
|
-
|
|
114
|
+
documentUri: string;
|
|
88
115
|
private languageId;
|
|
89
116
|
private documentVersion;
|
|
90
117
|
private allowHTMLContent;
|
|
91
118
|
private synchronizationMethod;
|
|
119
|
+
private changesTimeout;
|
|
120
|
+
private pendingChanges;
|
|
92
121
|
constructor(view: EditorView, { client, documentUri, languageId, allowHTMLContent, synchronizationMethod, }: {
|
|
93
122
|
client: LanguageServerClient;
|
|
94
123
|
documentUri: string;
|
|
@@ -162,5 +191,6 @@ export interface LanguageServerClientOptions<InitializationOptions = unknown> ex
|
|
|
162
191
|
transport: Transport;
|
|
163
192
|
autoClose?: boolean;
|
|
164
193
|
initializationOptions?: InitializationOptions;
|
|
194
|
+
locale?: string;
|
|
165
195
|
}
|
|
166
196
|
export {};
|
package/dist/rename.d.ts
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codemirror-languageserver",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.0",
|
|
4
4
|
"description": "Language Server Plugin for CodeMirror 6",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -40,13 +40,14 @@
|
|
|
40
40
|
"typescript": "^5.9.2"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
+
"marked": "^16.1.2",
|
|
44
|
+
"vscode-languageserver-protocol": "^3.17.5"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
43
47
|
"@codemirror/autocomplete": "^6.18.6",
|
|
44
48
|
"@codemirror/lint": "^6.8.5",
|
|
45
49
|
"@codemirror/state": "^6.5.2",
|
|
46
|
-
"@codemirror/view": "^6.38.1"
|
|
47
|
-
"@open-rpc/client-js": "^1.8.1",
|
|
48
|
-
"marked": "^16.1.2",
|
|
49
|
-
"vscode-languageserver-protocol": "^3.17.5"
|
|
50
|
+
"@codemirror/view": "^6.38.1"
|
|
50
51
|
},
|
|
51
52
|
"prettier": {
|
|
52
53
|
"tabWidth": 4,
|