codemirror-languageserver 1.6.0 → 1.7.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 +11 -3
- package/dist/index.d.ts +79 -1
- package/dist/index.js +101 -61
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,9 +16,17 @@ npm i codemirror-languageserver
|
|
|
16
16
|
import { languageServer } from 'codemirror-languageserver';
|
|
17
17
|
|
|
18
18
|
var ls = languageServer({
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
// WebSocket server URI and other client options.
|
|
20
|
+
serverUri: serverUri,
|
|
21
|
+
rootUri: 'file:///',
|
|
22
|
+
|
|
23
|
+
// Alternatively, to share the same client across multiple instances of this plugin.
|
|
24
|
+
client: new LanguageServerClient({
|
|
25
|
+
serverUri: serverUri,
|
|
26
|
+
rootUri: 'file:///'
|
|
27
|
+
}),
|
|
28
|
+
|
|
29
|
+
documentUri: `file:///${filename}`,
|
|
22
30
|
languageId: 'cpp' // As defined at https://microsoft.github.io/language-server-protocol/specification#textDocumentItem.
|
|
23
31
|
});
|
|
24
32
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,83 @@
|
|
|
1
|
+
import { EditorView, ViewUpdate, PluginValue } from '@codemirror/view';
|
|
1
2
|
import * as LSP from 'vscode-languageserver-protocol';
|
|
3
|
+
import { CompletionTriggerKind, PublishDiagnosticsParams } from "vscode-languageserver-protocol";
|
|
4
|
+
import { CompletionContext, CompletionResult } from '@codemirror/autocomplete';
|
|
5
|
+
import { Tooltip } from '@codemirror/tooltip';
|
|
6
|
+
interface LSPEventMap {
|
|
7
|
+
'textDocument/publishDiagnostics': LSP.PublishDiagnosticsParams;
|
|
8
|
+
}
|
|
9
|
+
type Notification = {
|
|
10
|
+
[key in keyof LSPEventMap]: {
|
|
11
|
+
jsonrpc: '2.0';
|
|
12
|
+
id?: null | undefined;
|
|
13
|
+
method: key;
|
|
14
|
+
params: LSPEventMap[key];
|
|
15
|
+
};
|
|
16
|
+
}[keyof LSPEventMap];
|
|
17
|
+
declare class LanguageServerClient {
|
|
18
|
+
private serverUri;
|
|
19
|
+
private rootUri;
|
|
20
|
+
private workspaceFolders;
|
|
21
|
+
private autoClose?;
|
|
22
|
+
private transport;
|
|
23
|
+
private requestManager;
|
|
24
|
+
private client;
|
|
25
|
+
ready: boolean;
|
|
26
|
+
capabilities: LSP.ServerCapabilities<any>;
|
|
27
|
+
private plugins;
|
|
28
|
+
constructor(options: LanguageServerClientOptions);
|
|
29
|
+
initialize(): Promise<void>;
|
|
30
|
+
close(): void;
|
|
31
|
+
textDocumentDidOpen(params: LSP.DidOpenTextDocumentParams): Promise<LSP.DidOpenTextDocumentParams>;
|
|
32
|
+
textDocumentDidChange(params: LSP.DidChangeTextDocumentParams): Promise<LSP.DidChangeTextDocumentParams>;
|
|
33
|
+
textDocumentHover(params: LSP.HoverParams): Promise<LSP.Hover>;
|
|
34
|
+
textDocumentCompletion(params: LSP.CompletionParams): Promise<LSP.CompletionList | LSP.CompletionItem[]>;
|
|
35
|
+
attachPlugin(plugin: LanguageServerPlugin): void;
|
|
36
|
+
detachPlugin(plugin: LanguageServerPlugin): void;
|
|
37
|
+
private request;
|
|
38
|
+
private notify;
|
|
39
|
+
private processRequest;
|
|
40
|
+
private processNotification;
|
|
41
|
+
}
|
|
42
|
+
declare class LanguageServerPlugin implements PluginValue {
|
|
43
|
+
private view;
|
|
44
|
+
client: LanguageServerClient;
|
|
45
|
+
private documentUri;
|
|
46
|
+
private languageId;
|
|
47
|
+
private documentVersion;
|
|
48
|
+
private changesTimeout;
|
|
49
|
+
constructor(view: EditorView);
|
|
50
|
+
update({ docChanged }: ViewUpdate): void;
|
|
51
|
+
destroy(): void;
|
|
52
|
+
initialize({ documentText }: {
|
|
53
|
+
documentText: string;
|
|
54
|
+
}): Promise<void>;
|
|
55
|
+
sendChange({ documentText }: {
|
|
56
|
+
documentText: string;
|
|
57
|
+
}): Promise<void>;
|
|
58
|
+
requestDiagnostics(view: EditorView): void;
|
|
59
|
+
requestHoverTooltip(view: EditorView, { line, character }: {
|
|
60
|
+
line: number;
|
|
61
|
+
character: number;
|
|
62
|
+
}): Promise<Tooltip | null>;
|
|
63
|
+
requestCompletion(context: CompletionContext, { line, character }: {
|
|
64
|
+
line: number;
|
|
65
|
+
character: number;
|
|
66
|
+
}, { triggerKind, triggerCharacter, }: {
|
|
67
|
+
triggerKind: CompletionTriggerKind;
|
|
68
|
+
triggerCharacter: string | undefined;
|
|
69
|
+
}): Promise<CompletionResult | null>;
|
|
70
|
+
processNotification(notification: Notification): void;
|
|
71
|
+
processDiagnostics(params: PublishDiagnosticsParams): void;
|
|
72
|
+
}
|
|
73
|
+
interface LanguageServerClientOptions {
|
|
74
|
+
serverUri: `ws://${string}` | `wss://${string}`;
|
|
75
|
+
rootUri: string | null;
|
|
76
|
+
workspaceFolders: LSP.WorkspaceFolder[] | null;
|
|
77
|
+
autoClose?: boolean;
|
|
78
|
+
}
|
|
2
79
|
interface LanguageServerOptions {
|
|
80
|
+
client?: LanguageServerClient;
|
|
3
81
|
serverUri: `ws://${string}` | `wss://${string}`;
|
|
4
82
|
rootUri: string | null;
|
|
5
83
|
workspaceFolders: LSP.WorkspaceFolder[] | null;
|
|
@@ -7,4 +85,4 @@ interface LanguageServerOptions {
|
|
|
7
85
|
languageId: string;
|
|
8
86
|
}
|
|
9
87
|
declare function languageServer(options: LanguageServerOptions): import("@codemirror/state").Extension[];
|
|
10
|
-
export { languageServer };
|
|
88
|
+
export { LanguageServerClient, languageServer };
|
package/dist/index.js
CHANGED
|
@@ -10,56 +10,30 @@ const timeout = 10000;
|
|
|
10
10
|
const changesDelay = 500;
|
|
11
11
|
const CompletionItemKindMap = Object.fromEntries(Object.entries(CompletionItemKind).map(([key, value]) => [value, key]));
|
|
12
12
|
const useLast = (values) => values.reduce((_, v) => v, '');
|
|
13
|
-
const
|
|
14
|
-
const rootUri = Facet.define({ combine: useLast });
|
|
15
|
-
const workspaceFolders = Facet.define({ combine: useLast });
|
|
13
|
+
const client = Facet.define({ combine: useLast });
|
|
16
14
|
const documentUri = Facet.define({ combine: useLast });
|
|
17
15
|
const languageId = Facet.define({ combine: useLast });
|
|
18
|
-
class
|
|
19
|
-
constructor(
|
|
20
|
-
this.
|
|
21
|
-
this.rootUri =
|
|
22
|
-
this.workspaceFolders =
|
|
23
|
-
this.
|
|
24
|
-
this.
|
|
25
|
-
this.
|
|
26
|
-
this.changesTimeout = 0;
|
|
27
|
-
this.transport = new WebSocketTransport(this.view.state.facet(serverUri));
|
|
16
|
+
class LanguageServerClient {
|
|
17
|
+
constructor(options) {
|
|
18
|
+
this.serverUri = options.serverUri;
|
|
19
|
+
this.rootUri = options.rootUri;
|
|
20
|
+
this.workspaceFolders = options.workspaceFolders;
|
|
21
|
+
this.autoClose = options.autoClose;
|
|
22
|
+
this.plugins = [];
|
|
23
|
+
this.transport = new WebSocketTransport(this.serverUri);
|
|
28
24
|
this.requestManager = new RequestManager([this.transport]);
|
|
29
25
|
this.client = new Client(this.requestManager);
|
|
30
26
|
this.client.onNotification((data) => {
|
|
31
27
|
this.processNotification(data);
|
|
32
28
|
});
|
|
33
|
-
this.initialize({
|
|
34
|
-
documentText: this.view.state.doc.toString(),
|
|
35
|
-
});
|
|
36
29
|
this.transport.connection.addEventListener('message', (message) => {
|
|
37
30
|
const data = JSON.parse(message.data);
|
|
38
31
|
if (data.method && data.id)
|
|
39
32
|
this.processRequest(data);
|
|
40
33
|
});
|
|
34
|
+
this.initialize();
|
|
41
35
|
}
|
|
42
|
-
|
|
43
|
-
if (!docChanged)
|
|
44
|
-
return;
|
|
45
|
-
if (this.changesTimeout)
|
|
46
|
-
clearTimeout(this.changesTimeout);
|
|
47
|
-
this.changesTimeout = self.setTimeout(() => {
|
|
48
|
-
this.sendChange({
|
|
49
|
-
documentText: this.view.state.doc.toString(),
|
|
50
|
-
});
|
|
51
|
-
}, changesDelay);
|
|
52
|
-
}
|
|
53
|
-
destroy() {
|
|
54
|
-
this.client.close();
|
|
55
|
-
}
|
|
56
|
-
request(method, params, timeout) {
|
|
57
|
-
return this.client.request({ method, params }, timeout);
|
|
58
|
-
}
|
|
59
|
-
notify(method, params) {
|
|
60
|
-
return this.client.notify({ method, params });
|
|
61
|
-
}
|
|
62
|
-
async initialize({ documentText }) {
|
|
36
|
+
async initialize() {
|
|
63
37
|
const { capabilities } = await this.request('initialize', {
|
|
64
38
|
capabilities: {
|
|
65
39
|
textDocument: {
|
|
@@ -121,21 +95,94 @@ class LanguageServerPlugin {
|
|
|
121
95
|
}, timeout * 3);
|
|
122
96
|
this.capabilities = capabilities;
|
|
123
97
|
this.notify('initialized', {});
|
|
124
|
-
this.
|
|
98
|
+
this.ready = true;
|
|
99
|
+
}
|
|
100
|
+
close() {
|
|
101
|
+
this.client.close();
|
|
102
|
+
}
|
|
103
|
+
textDocumentDidOpen(params) {
|
|
104
|
+
return this.notify('textDocument/didOpen', params);
|
|
105
|
+
}
|
|
106
|
+
textDocumentDidChange(params) {
|
|
107
|
+
return this.notify('textDocument/didChange', params);
|
|
108
|
+
}
|
|
109
|
+
async textDocumentHover(params) {
|
|
110
|
+
return await this.request('textDocument/hover', params, timeout);
|
|
111
|
+
}
|
|
112
|
+
async textDocumentCompletion(params) {
|
|
113
|
+
return await this.request('textDocument/completion', params, timeout);
|
|
114
|
+
}
|
|
115
|
+
attachPlugin(plugin) {
|
|
116
|
+
this.plugins.push(plugin);
|
|
117
|
+
}
|
|
118
|
+
detachPlugin(plugin) {
|
|
119
|
+
const i = this.plugins.indexOf(plugin);
|
|
120
|
+
if (i === -1)
|
|
121
|
+
return;
|
|
122
|
+
this.plugins.splice(i, 1);
|
|
123
|
+
if (this.autoClose)
|
|
124
|
+
this.close();
|
|
125
|
+
}
|
|
126
|
+
request(method, params, timeout) {
|
|
127
|
+
return this.client.request({ method, params }, timeout);
|
|
128
|
+
}
|
|
129
|
+
notify(method, params) {
|
|
130
|
+
return this.client.notify({ method, params });
|
|
131
|
+
}
|
|
132
|
+
processRequest({ id }) {
|
|
133
|
+
this.transport.connection.send(JSON.stringify({
|
|
134
|
+
jsonrpc: '2.0',
|
|
135
|
+
id,
|
|
136
|
+
result: null
|
|
137
|
+
}));
|
|
138
|
+
}
|
|
139
|
+
processNotification(notification) {
|
|
140
|
+
for (const plugin of this.plugins)
|
|
141
|
+
plugin.processNotification(notification);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
class LanguageServerPlugin {
|
|
145
|
+
constructor(view) {
|
|
146
|
+
this.view = view;
|
|
147
|
+
this.client = this.view.state.facet(client);
|
|
148
|
+
this.documentUri = this.view.state.facet(documentUri);
|
|
149
|
+
this.languageId = this.view.state.facet(languageId);
|
|
150
|
+
this.documentVersion = 0;
|
|
151
|
+
this.changesTimeout = 0;
|
|
152
|
+
this.client.attachPlugin(this);
|
|
153
|
+
this.initialize({
|
|
154
|
+
documentText: this.view.state.doc.toString(),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
update({ docChanged }) {
|
|
158
|
+
if (!docChanged)
|
|
159
|
+
return;
|
|
160
|
+
if (this.changesTimeout)
|
|
161
|
+
clearTimeout(this.changesTimeout);
|
|
162
|
+
this.changesTimeout = self.setTimeout(() => {
|
|
163
|
+
this.sendChange({
|
|
164
|
+
documentText: this.view.state.doc.toString(),
|
|
165
|
+
});
|
|
166
|
+
}, changesDelay);
|
|
167
|
+
}
|
|
168
|
+
destroy() {
|
|
169
|
+
this.client.detachPlugin(this);
|
|
170
|
+
}
|
|
171
|
+
async initialize({ documentText }) {
|
|
172
|
+
this.client.textDocumentDidOpen({
|
|
125
173
|
textDocument: {
|
|
126
174
|
uri: this.documentUri,
|
|
127
175
|
languageId: this.languageId,
|
|
128
176
|
text: documentText,
|
|
129
177
|
version: this.documentVersion,
|
|
130
|
-
}
|
|
178
|
+
}
|
|
131
179
|
});
|
|
132
|
-
this.ready = true;
|
|
133
180
|
}
|
|
134
181
|
async sendChange({ documentText }) {
|
|
135
|
-
if (!this.ready)
|
|
182
|
+
if (!this.client.ready)
|
|
136
183
|
return;
|
|
137
184
|
try {
|
|
138
|
-
await this.
|
|
185
|
+
await this.client.textDocumentDidChange({
|
|
139
186
|
textDocument: {
|
|
140
187
|
uri: this.documentUri,
|
|
141
188
|
version: this.documentVersion++,
|
|
@@ -151,13 +198,13 @@ class LanguageServerPlugin {
|
|
|
151
198
|
this.sendChange({ documentText: view.state.doc.toString() });
|
|
152
199
|
}
|
|
153
200
|
async requestHoverTooltip(view, { line, character }) {
|
|
154
|
-
if (!this.ready || !this.capabilities.hoverProvider)
|
|
201
|
+
if (!this.client.ready || !this.client.capabilities.hoverProvider)
|
|
155
202
|
return null;
|
|
156
203
|
this.sendChange({ documentText: view.state.doc.toString() });
|
|
157
|
-
const result = await this.
|
|
204
|
+
const result = await this.client.textDocumentHover({
|
|
158
205
|
textDocument: { uri: this.documentUri },
|
|
159
206
|
position: { line, character },
|
|
160
|
-
}
|
|
207
|
+
});
|
|
161
208
|
if (!result)
|
|
162
209
|
return null;
|
|
163
210
|
const { contents, range } = result;
|
|
@@ -175,19 +222,19 @@ class LanguageServerPlugin {
|
|
|
175
222
|
return { pos, end, create: (view) => ({ dom }), above: true };
|
|
176
223
|
}
|
|
177
224
|
async requestCompletion(context, { line, character }, { triggerKind, triggerCharacter, }) {
|
|
178
|
-
if (!this.ready || !this.capabilities.completionProvider)
|
|
225
|
+
if (!this.client.ready || !this.client.capabilities.completionProvider)
|
|
179
226
|
return null;
|
|
180
227
|
this.sendChange({
|
|
181
228
|
documentText: context.state.doc.toString(),
|
|
182
229
|
});
|
|
183
|
-
const result = await this.
|
|
230
|
+
const result = await this.client.textDocumentCompletion({
|
|
184
231
|
textDocument: { uri: this.documentUri },
|
|
185
232
|
position: { line, character },
|
|
186
233
|
context: {
|
|
187
234
|
triggerKind,
|
|
188
235
|
triggerCharacter,
|
|
189
|
-
}
|
|
190
|
-
}
|
|
236
|
+
}
|
|
237
|
+
});
|
|
191
238
|
if (!result)
|
|
192
239
|
return null;
|
|
193
240
|
const items = 'items' in result ? result.items : result;
|
|
@@ -233,13 +280,6 @@ class LanguageServerPlugin {
|
|
|
233
280
|
options,
|
|
234
281
|
};
|
|
235
282
|
}
|
|
236
|
-
processRequest({ id }) {
|
|
237
|
-
this.transport.connection.send(JSON.stringify({
|
|
238
|
-
jsonrpc: '2.0',
|
|
239
|
-
id,
|
|
240
|
-
result: null
|
|
241
|
-
}));
|
|
242
|
-
}
|
|
243
283
|
processNotification(notification) {
|
|
244
284
|
try {
|
|
245
285
|
switch (notification.method) {
|
|
@@ -252,6 +292,8 @@ class LanguageServerPlugin {
|
|
|
252
292
|
}
|
|
253
293
|
}
|
|
254
294
|
processDiagnostics(params) {
|
|
295
|
+
if (params.uri !== this.documentUri)
|
|
296
|
+
return;
|
|
255
297
|
const diagnostics = params.diagnostics
|
|
256
298
|
.map(({ range, message, severity }) => ({
|
|
257
299
|
from: posToOffset(this.view.state.doc, range.start),
|
|
@@ -280,9 +322,7 @@ class LanguageServerPlugin {
|
|
|
280
322
|
function languageServer(options) {
|
|
281
323
|
let plugin = null;
|
|
282
324
|
return [
|
|
283
|
-
|
|
284
|
-
workspaceFolders.of(options.workspaceFolders),
|
|
285
|
-
rootUri.of(options.rootUri),
|
|
325
|
+
client.of(options.client || new LanguageServerClient({ ...options, autoClose: true })),
|
|
286
326
|
documentUri.of(options.documentUri),
|
|
287
327
|
languageId.of(options.languageId),
|
|
288
328
|
ViewPlugin.define((view) => (plugin = new LanguageServerPlugin(view))),
|
|
@@ -301,7 +341,7 @@ function languageServer(options) {
|
|
|
301
341
|
let trigKind = CompletionTriggerKind.Invoked;
|
|
302
342
|
let trigChar;
|
|
303
343
|
if (!explicit &&
|
|
304
|
-
((_c = (_b = (_a = plugin.capabilities) === null || _a === void 0 ? void 0 : _a.completionProvider) === null || _b === void 0 ? void 0 : _b.triggerCharacters) === null || _c === void 0 ? void 0 : _c.includes(line.text[pos - line.from - 1]))) {
|
|
344
|
+
((_c = (_b = (_a = plugin.client.capabilities) === null || _a === void 0 ? void 0 : _a.completionProvider) === null || _b === void 0 ? void 0 : _b.triggerCharacters) === null || _c === void 0 ? void 0 : _c.includes(line.text[pos - line.from - 1]))) {
|
|
305
345
|
trigKind = CompletionTriggerKind.TriggerCharacter;
|
|
306
346
|
trigChar = line.text[pos - line.from - 1];
|
|
307
347
|
}
|
|
@@ -381,4 +421,4 @@ const baseTheme = EditorView.baseTheme({
|
|
|
381
421
|
},
|
|
382
422
|
});
|
|
383
423
|
|
|
384
|
-
export { languageServer };
|
|
424
|
+
export { LanguageServerClient, languageServer };
|