codemirror-languageserver 1.5.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 CHANGED
@@ -16,9 +16,17 @@ npm i codemirror-languageserver
16
16
  import { languageServer } from 'codemirror-languageserver';
17
17
 
18
18
  var ls = languageServer({
19
- serverUri: serverUri
20
- rootUri: 'file:///'
21
- documentUri: `file:///${filename}`
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,8 +1,88 @@
1
+ import { EditorView, ViewUpdate, PluginValue } from '@codemirror/view';
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
+ }
1
79
  interface LanguageServerOptions {
80
+ client?: LanguageServerClient;
2
81
  serverUri: `ws://${string}` | `wss://${string}`;
3
- rootUri: string;
82
+ rootUri: string | null;
83
+ workspaceFolders: LSP.WorkspaceFolder[] | null;
4
84
  documentUri: string;
5
85
  languageId: string;
6
86
  }
7
87
  declare function languageServer(options: LanguageServerOptions): import("@codemirror/state").Extension[];
8
- export { languageServer };
88
+ export { LanguageServerClient, languageServer };
package/dist/index.js CHANGED
@@ -10,49 +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 serverUri = Facet.define({ combine: useLast });
14
- const rootUri = Facet.define({ combine: useLast });
13
+ const client = Facet.define({ combine: useLast });
15
14
  const documentUri = Facet.define({ combine: useLast });
16
15
  const languageId = Facet.define({ combine: useLast });
17
- class LanguageServerPlugin {
18
- constructor(view) {
19
- this.view = view;
20
- this.rootUri = this.view.state.facet(rootUri);
21
- this.documentUri = this.view.state.facet(documentUri);
22
- this.languageId = this.view.state.facet(languageId);
23
- this.documentVersion = 0;
24
- this.changesTimeout = 0;
25
- 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);
26
24
  this.requestManager = new RequestManager([this.transport]);
27
25
  this.client = new Client(this.requestManager);
28
26
  this.client.onNotification((data) => {
29
27
  this.processNotification(data);
30
28
  });
31
- this.initialize({
32
- documentText: this.view.state.doc.toString(),
29
+ this.transport.connection.addEventListener('message', (message) => {
30
+ const data = JSON.parse(message.data);
31
+ if (data.method && data.id)
32
+ this.processRequest(data);
33
33
  });
34
+ this.initialize();
34
35
  }
35
- update({ docChanged }) {
36
- if (!docChanged)
37
- return;
38
- if (this.changesTimeout)
39
- clearTimeout(this.changesTimeout);
40
- this.changesTimeout = self.setTimeout(() => {
41
- this.sendChange({
42
- documentText: this.view.state.doc.toString(),
43
- });
44
- }, changesDelay);
45
- }
46
- destroy() {
47
- this.client.close();
48
- }
49
- request(method, params, timeout) {
50
- return this.client.request({ method, params }, timeout);
51
- }
52
- notify(method, params) {
53
- return this.client.notify({ method, params });
54
- }
55
- async initialize({ documentText }) {
36
+ async initialize() {
56
37
  const { capabilities } = await this.request('initialize', {
57
38
  capabilities: {
58
39
  textDocument: {
@@ -110,30 +91,98 @@ class LanguageServerPlugin {
110
91
  initializationOptions: null,
111
92
  processId: null,
112
93
  rootUri: this.rootUri,
113
- workspaceFolders: [
114
- {
115
- name: 'root',
116
- uri: this.rootUri,
117
- },
118
- ],
94
+ workspaceFolders: this.workspaceFolders,
119
95
  }, timeout * 3);
120
96
  this.capabilities = capabilities;
121
97
  this.notify('initialized', {});
122
- this.notify('textDocument/didOpen', {
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({
123
173
  textDocument: {
124
174
  uri: this.documentUri,
125
175
  languageId: this.languageId,
126
176
  text: documentText,
127
177
  version: this.documentVersion,
128
- },
178
+ }
129
179
  });
130
- this.ready = true;
131
180
  }
132
181
  async sendChange({ documentText }) {
133
- if (!this.ready)
182
+ if (!this.client.ready)
134
183
  return;
135
184
  try {
136
- await this.notify('textDocument/didChange', {
185
+ await this.client.textDocumentDidChange({
137
186
  textDocument: {
138
187
  uri: this.documentUri,
139
188
  version: this.documentVersion++,
@@ -149,13 +198,13 @@ class LanguageServerPlugin {
149
198
  this.sendChange({ documentText: view.state.doc.toString() });
150
199
  }
151
200
  async requestHoverTooltip(view, { line, character }) {
152
- if (!this.ready || !this.capabilities.hoverProvider)
201
+ if (!this.client.ready || !this.client.capabilities.hoverProvider)
153
202
  return null;
154
203
  this.sendChange({ documentText: view.state.doc.toString() });
155
- const result = await this.request('textDocument/hover', {
204
+ const result = await this.client.textDocumentHover({
156
205
  textDocument: { uri: this.documentUri },
157
206
  position: { line, character },
158
- }, timeout);
207
+ });
159
208
  if (!result)
160
209
  return null;
161
210
  const { contents, range } = result;
@@ -173,19 +222,19 @@ class LanguageServerPlugin {
173
222
  return { pos, end, create: (view) => ({ dom }), above: true };
174
223
  }
175
224
  async requestCompletion(context, { line, character }, { triggerKind, triggerCharacter, }) {
176
- if (!this.ready || !this.capabilities.completionProvider)
225
+ if (!this.client.ready || !this.client.capabilities.completionProvider)
177
226
  return null;
178
227
  this.sendChange({
179
228
  documentText: context.state.doc.toString(),
180
229
  });
181
- const result = await this.request('textDocument/completion', {
230
+ const result = await this.client.textDocumentCompletion({
182
231
  textDocument: { uri: this.documentUri },
183
232
  position: { line, character },
184
233
  context: {
185
234
  triggerKind,
186
235
  triggerCharacter,
187
- },
188
- }, timeout);
236
+ }
237
+ });
189
238
  if (!result)
190
239
  return null;
191
240
  const items = 'items' in result ? result.items : result;
@@ -243,6 +292,8 @@ class LanguageServerPlugin {
243
292
  }
244
293
  }
245
294
  processDiagnostics(params) {
295
+ if (params.uri !== this.documentUri)
296
+ return;
246
297
  const diagnostics = params.diagnostics
247
298
  .map(({ range, message, severity }) => ({
248
299
  from: posToOffset(this.view.state.doc, range.start),
@@ -271,8 +322,7 @@ class LanguageServerPlugin {
271
322
  function languageServer(options) {
272
323
  let plugin = null;
273
324
  return [
274
- serverUri.of(options.serverUri),
275
- rootUri.of(options.rootUri),
325
+ client.of(options.client || new LanguageServerClient({ ...options, autoClose: true })),
276
326
  documentUri.of(options.documentUri),
277
327
  languageId.of(options.languageId),
278
328
  ViewPlugin.define((view) => (plugin = new LanguageServerPlugin(view))),
@@ -291,7 +341,7 @@ function languageServer(options) {
291
341
  let trigKind = CompletionTriggerKind.Invoked;
292
342
  let trigChar;
293
343
  if (!explicit &&
294
- ((_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]))) {
295
345
  trigKind = CompletionTriggerKind.TriggerCharacter;
296
346
  trigChar = line.text[pos - line.from - 1];
297
347
  }
@@ -371,4 +421,4 @@ const baseTheme = EditorView.baseTheme({
371
421
  },
372
422
  });
373
423
 
374
- export { languageServer };
424
+ export { LanguageServerClient, languageServer };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codemirror-languageserver",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "Language Server Plugin for CodeMirror 6",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -32,10 +32,10 @@
32
32
  "dependencies": {
33
33
  "@codemirror/autocomplete": "^0.19.8",
34
34
  "@codemirror/lint": "^0.19.3",
35
- "@codemirror/state": "^0.19.6",
36
- "@codemirror/tooltip": "^0.19.7",
37
- "@codemirror/view": "^0.19.19",
38
- "@open-rpc/client-js": "^1.6.2",
35
+ "@codemirror/state": "^0.19.8",
36
+ "@codemirror/tooltip": "^0.19.14",
37
+ "@codemirror/view": "^0.19.43",
38
+ "@open-rpc/client-js": "^1.7.1",
39
39
  "vscode-languageserver-protocol": "^3.16.0"
40
40
  },
41
41
  "prettier": {
@@ -1,10 +0,0 @@
1
- interface LanguageServerOptions {
2
- serverUri: `ws://${string}` | `wss://${string}`;
3
- rootUri: string;
4
- documentUri: string;
5
- languageId: string;
6
- onConnect(): void;
7
- onDisconnect(): void;
8
- }
9
- declare function languageServer(options: LanguageServerOptions): import("@codemirror/state").Extension[];
10
- export { languageServer };