codemirror-languageserver 1.18.1 → 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 +548 -66
- 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,6 +347,8 @@ 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;
|
|
@@ -230,20 +365,40 @@ class LanguageServerPlugin {
|
|
|
230
365
|
if (!docChanged) {
|
|
231
366
|
return;
|
|
232
367
|
}
|
|
368
|
+
if (this.changesTimeout) {
|
|
369
|
+
clearTimeout(this.changesTimeout);
|
|
370
|
+
}
|
|
233
371
|
switch (this.synchronizationMethod) {
|
|
234
372
|
case SynchronizationMethod.Full:
|
|
235
|
-
this.
|
|
373
|
+
this.pendingChanges = [
|
|
236
374
|
{
|
|
237
375
|
text: this.view.state.doc.toString(),
|
|
238
376
|
},
|
|
239
|
-
]
|
|
377
|
+
];
|
|
240
378
|
break;
|
|
241
379
|
case SynchronizationMethod.Incremental:
|
|
242
|
-
this.
|
|
380
|
+
if (!this.pendingChanges) {
|
|
381
|
+
this.pendingChanges = [];
|
|
382
|
+
}
|
|
383
|
+
this.pendingChanges.push(...changeSetToEvents(startState.doc, changes));
|
|
243
384
|
break;
|
|
244
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);
|
|
245
393
|
}
|
|
246
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
|
+
}
|
|
247
402
|
this.client.detachPlugin(this);
|
|
248
403
|
}
|
|
249
404
|
async initialize({ documentText }) {
|
|
@@ -302,11 +457,12 @@ class LanguageServerPlugin {
|
|
|
302
457
|
}
|
|
303
458
|
const dom = document.createElement('div');
|
|
304
459
|
dom.classList.add('documentation');
|
|
305
|
-
|
|
306
|
-
|
|
460
|
+
const formatted = await formatContents(contents);
|
|
461
|
+
if (formatted.isHTML || this.allowHTMLContent) {
|
|
462
|
+
dom.innerHTML = formatted.value;
|
|
307
463
|
}
|
|
308
464
|
else {
|
|
309
|
-
dom.textContent =
|
|
465
|
+
dom.textContent = formatted.value;
|
|
310
466
|
}
|
|
311
467
|
return {
|
|
312
468
|
pos,
|
|
@@ -366,56 +522,52 @@ class LanguageServerPlugin {
|
|
|
366
522
|
label,
|
|
367
523
|
detail,
|
|
368
524
|
apply(view, completion, from, to) {
|
|
525
|
+
const changes = [];
|
|
369
526
|
if (isLSPTextEdit(textEdit)) {
|
|
370
|
-
|
|
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
|
+
});
|
|
371
532
|
}
|
|
372
533
|
else {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
534
|
+
changes.push({
|
|
535
|
+
from,
|
|
536
|
+
to,
|
|
537
|
+
insert: label,
|
|
538
|
+
});
|
|
377
539
|
}
|
|
378
|
-
additionalTextEdits
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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
|
+
});
|
|
383
547
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
return -1;
|
|
387
|
-
}
|
|
388
|
-
return 0;
|
|
389
|
-
})
|
|
390
|
-
.forEach((textEdit) => {
|
|
391
|
-
view.dispatch(view.state.update({
|
|
392
|
-
changes: {
|
|
393
|
-
from: posToOffset(view.state.doc, textEdit.range.start),
|
|
394
|
-
to: posToOffset(view.state.doc, textEdit.range.end),
|
|
395
|
-
insert: textEdit.newText,
|
|
396
|
-
},
|
|
397
|
-
}));
|
|
398
|
-
});
|
|
548
|
+
}
|
|
549
|
+
view.dispatch(view.state.update({ changes }));
|
|
399
550
|
},
|
|
400
551
|
type: kind && CompletionItemKindMap[kind].toLowerCase(),
|
|
401
552
|
};
|
|
402
553
|
completion.info = async () => {
|
|
403
554
|
var _a;
|
|
404
|
-
|
|
405
|
-
if (!documentation &&
|
|
555
|
+
if (!item.documentation &&
|
|
406
556
|
((_a = this.client.capabilities.completionProvider) === null || _a === void 0 ? void 0 : _a.resolveProvider)) {
|
|
407
557
|
const resolvedItem = await this.client.completionItemResolve(item);
|
|
408
|
-
documentation = resolvedItem.documentation;
|
|
558
|
+
item.documentation = resolvedItem.documentation;
|
|
409
559
|
}
|
|
560
|
+
const { documentation } = item;
|
|
410
561
|
if (!documentation)
|
|
411
562
|
return null;
|
|
412
563
|
const dom = document.createElement('div');
|
|
413
564
|
dom.classList.add('documentation');
|
|
414
|
-
|
|
415
|
-
|
|
565
|
+
const formatted = await formatContents(documentation);
|
|
566
|
+
if (formatted.isHTML || this.allowHTMLContent) {
|
|
567
|
+
dom.innerHTML = formatted.value;
|
|
416
568
|
}
|
|
417
569
|
else {
|
|
418
|
-
dom.textContent =
|
|
570
|
+
dom.textContent = formatted.value;
|
|
419
571
|
}
|
|
420
572
|
return dom;
|
|
421
573
|
};
|
|
@@ -511,19 +663,32 @@ class LanguageServerPlugin {
|
|
|
511
663
|
const languageServerPlugin = ViewPlugin.fromClass(LanguageServerPlugin);
|
|
512
664
|
async function formatContents(contents) {
|
|
513
665
|
if (isLSPMarkupContent(contents)) {
|
|
514
|
-
let value = contents.value;
|
|
515
666
|
if (contents.kind === 'markdown') {
|
|
516
|
-
|
|
667
|
+
return {
|
|
668
|
+
value: await marked.parse(contents.value),
|
|
669
|
+
isHTML: true,
|
|
670
|
+
};
|
|
517
671
|
}
|
|
518
|
-
return value;
|
|
672
|
+
return { value: contents.value, isHTML: false };
|
|
519
673
|
}
|
|
520
674
|
else if (Array.isArray(contents)) {
|
|
521
|
-
|
|
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 };
|
|
522
681
|
}
|
|
523
682
|
else if (typeof contents === 'string') {
|
|
524
|
-
return contents;
|
|
683
|
+
return { value: contents, isHTML: false };
|
|
525
684
|
}
|
|
526
685
|
}
|
|
686
|
+
function escapeHTML(text) {
|
|
687
|
+
return text
|
|
688
|
+
.replace(/&/g, '&')
|
|
689
|
+
.replace(/</g, '<')
|
|
690
|
+
.replace(/>/g, '>');
|
|
691
|
+
}
|
|
527
692
|
function toSet(chars) {
|
|
528
693
|
let preamble = '';
|
|
529
694
|
let flat = Array.from(chars).join('');
|
|
@@ -579,6 +744,223 @@ const jumpToDefinitionKeymap = [
|
|
|
579
744
|
{ key: 'Mod-F12', run: jumpToTypeDefinition, preventDefault: true },
|
|
580
745
|
];
|
|
581
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
|
+
|
|
582
964
|
const hoverTooltip = () => hoverTooltip$1((view, pos) => {
|
|
583
965
|
var _a;
|
|
584
966
|
const plugin = view.plugin(languageServerPlugin);
|
|
@@ -601,7 +983,8 @@ const autocompletion = () => autocompletion$1({
|
|
|
601
983
|
trigKind = CompletionTriggerKind.TriggerCharacter;
|
|
602
984
|
trigChar = line.text[pos - line.from - 1];
|
|
603
985
|
}
|
|
604
|
-
if (
|
|
986
|
+
if (!explicit &&
|
|
987
|
+
trigKind === CompletionTriggerKind.Invoked &&
|
|
605
988
|
!context.matchBefore(/\w+$/)) {
|
|
606
989
|
return null;
|
|
607
990
|
}
|
|
@@ -613,6 +996,103 @@ const autocompletion = () => autocompletion$1({
|
|
|
613
996
|
],
|
|
614
997
|
});
|
|
615
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
|
+
|
|
616
1096
|
const mouseHandler = () => EditorView.domEventHandlers({
|
|
617
1097
|
mousedown: (event, view) => {
|
|
618
1098
|
if (!event.ctrlKey && !event.metaKey)
|
|
@@ -652,9 +1132,11 @@ function languageServerWithTransport(options) {
|
|
|
652
1132
|
}),
|
|
653
1133
|
hoverTooltip(),
|
|
654
1134
|
autocompletion(),
|
|
1135
|
+
documentHighlight(),
|
|
1136
|
+
renameExtension(),
|
|
655
1137
|
keymap.of([...jumpToDefinitionKeymap]),
|
|
656
1138
|
mouseHandler(),
|
|
657
1139
|
];
|
|
658
1140
|
}
|
|
659
1141
|
|
|
660
|
-
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,
|