codemirror-languageserver 1.14.0 → 1.15.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
@@ -12,37 +12,48 @@ This plugin enables code completion, hover tooltips, and linter functionality by
12
12
  npm i codemirror-languageserver
13
13
  ```
14
14
 
15
- ``` js
15
+ ```js
16
16
  import { languageServer } from 'codemirror-languageserver';
17
17
 
18
- const transport = new WebSocketTransport(serverUri)
18
+ const transport = new WebSocketTransport(serverUri);
19
19
 
20
20
  var ls = languageServer({
21
- // WebSocket server uri and other client options.
22
- serverUri,
23
- rootUri: 'file:///',
24
-
25
- // Alternatively, to share the same client across multiple instances of this plugin.
26
- client: new LanguageServerClient({
27
- serverUri,
28
- rootUri: 'file:///'
29
- }),
30
-
31
- documentUri: `file:///${filename}`,
32
- languageId: 'cpp' // As defined at https://microsoft.github.io/language-server-protocol/specification#textDocumentItem.
21
+ // WebSocket server uri and other client options.
22
+ serverUri,
23
+ rootUri: 'file:///',
24
+
25
+ // Alternatively, to share the same client across multiple instances of this plugin.
26
+ client: new LanguageServerClient({
27
+ serverUri,
28
+ rootUri: 'file:///',
29
+ }),
30
+
31
+ documentUri: `file:///${filename}`,
32
+ languageId: 'cpp', // As defined at https://microsoft.github.io/language-server-protocol/specification#textDocumentItem.
33
33
  });
34
34
 
35
35
  var view = new EditorView({
36
- state: EditorState.create({
37
- extensions: [
38
- // ...
39
- ls,
40
- // ...
41
- ]
42
- })
36
+ state: EditorState.create({
37
+ extensions: [
38
+ // ...
39
+ ls,
40
+ // ...
41
+ ],
42
+ }),
43
43
  });
44
44
  ```
45
45
 
46
+ ### Using with Initialization Options
47
+
48
+ The plugin includes built-in TypeScript definitions for popular language servers:
49
+
50
+ - `PyrightInitializationOptions` - Python (Pyright)
51
+ - `RustAnalyzerInitializationOptions` - Rust (rust-analyzer)
52
+ - `TypeScriptInitializationOptions` - TypeScript/JavaScript
53
+ - `ESLintInitializationOptions` - ESLint
54
+ - `ClangdInitializationOptions` - C/C++ (Clangd)
55
+ - `GoplsInitializationOptions` - Go (Gopls)
56
+
46
57
  ## Contributing
47
58
 
48
59
  Contributions are welcome.
@@ -51,7 +62,7 @@ Contributions are welcome.
51
62
 
52
63
  https://user-images.githubusercontent.com/348107/120141150-c6bb9180-c1fd-11eb-8ada-9b7b7a1e4ade.mp4
53
64
 
54
- - [Toph](https://toph.co): Competitive programming platform. Toph uses Language Server Plugin for CodeMirror 6 with its integrated code editor.
65
+ - [Toph](https://toph.co): Competitive programming platform. Toph uses Language Server Plugin for CodeMirror 6 with its integrated code editor.
55
66
 
56
67
  ## License
57
68
 
package/demo/demo.js CHANGED
@@ -32,7 +32,11 @@ import {
32
32
  } from '@codemirror/autocomplete';
33
33
  import { lintKeymap } from '@codemirror/lint';
34
34
  import { cpp } from '@codemirror/lang-cpp';
35
- import { languageServer } from '../src';
35
+ import {
36
+ languageServer,
37
+ jumpToDefinitionKeymap,
38
+ jumpToDefinitionPos,
39
+ } from '../src';
36
40
 
37
41
  const tabSizeCompartment = new Compartment();
38
42
 
@@ -80,5 +84,17 @@ const view = new EditorView({
80
84
  documentUri: `file:///demo/main.cpp`,
81
85
  languageId: 'cpp',
82
86
  }),
87
+ keymap.of([...jumpToDefinitionKeymap]),
88
+ EditorView.domEventHandlers({
89
+ click: (event, view) => {
90
+ if (!event.ctrlKey && !event.metaKey) return;
91
+ const pos = view.posAtCoords({
92
+ x: event.clientX,
93
+ y: event.clientY,
94
+ });
95
+ const ok = jumpToDefinitionPos(pos)(view);
96
+ if (ok) event.preventDefault();
97
+ },
98
+ }),
83
99
  ],
84
100
  });
package/demo/dist/demo.js CHANGED
@@ -39824,6 +39824,24 @@ d.options;
39824
39824
  R.parse;
39825
39825
  b.lex;
39826
39826
 
39827
+ function posToOffset(doc, pos) {
39828
+ if (pos.line >= doc.lines) {
39829
+ return;
39830
+ }
39831
+ const offset = doc.line(pos.line + 1).from + pos.character;
39832
+ if (offset > doc.length) {
39833
+ return;
39834
+ }
39835
+ return offset;
39836
+ }
39837
+ function offsetToPos(doc, offset) {
39838
+ const line = doc.lineAt(offset);
39839
+ return {
39840
+ character: offset - line.from,
39841
+ line: line.number - 1,
39842
+ };
39843
+ }
39844
+
39827
39845
  const timeout = 10000;
39828
39846
  const changesDelay = 500;
39829
39847
  const CompletionItemKindMap = Object.fromEntries(Object.entries(mainExports.CompletionItemKind).map(([key, value]) => [value, key]));
@@ -39835,6 +39853,7 @@ const documentUri = Facet.define({ combine: useLast });
39835
39853
  const languageId = Facet.define({ combine: useLast });
39836
39854
  class LanguageServerClient {
39837
39855
  constructor(options) {
39856
+ this.options = options;
39838
39857
  this.rootUri = options.rootUri;
39839
39858
  this.workspaceFolders = options.workspaceFolders;
39840
39859
  this.autoClose = options.autoClose;
@@ -39862,8 +39881,9 @@ class LanguageServerClient {
39862
39881
  }
39863
39882
  this.initializePromise = this.initialize();
39864
39883
  }
39865
- async initialize() {
39866
- const { capabilities } = await this.request('initialize', {
39884
+ getInitializeParams() {
39885
+ var _a;
39886
+ return {
39867
39887
  capabilities: {
39868
39888
  textDocument: {
39869
39889
  hover: {
@@ -39886,7 +39906,7 @@ class LanguageServerClient {
39886
39906
  deprecatedSupport: false,
39887
39907
  preselectSupport: false,
39888
39908
  },
39889
- contextSupport: false,
39909
+ contextSupport: true,
39890
39910
  },
39891
39911
  signatureHelp: {
39892
39912
  dynamicRegistration: true,
@@ -39917,11 +39937,14 @@ class LanguageServerClient {
39917
39937
  },
39918
39938
  },
39919
39939
  },
39920
- initializationOptions: null,
39940
+ initializationOptions: (_a = this.options.initializationOptions) !== null && _a !== void 0 ? _a : null,
39921
39941
  processId: null,
39922
39942
  rootUri: this.rootUri,
39923
39943
  workspaceFolders: this.workspaceFolders,
39924
- }, timeout * 3);
39944
+ };
39945
+ }
39946
+ async initialize() {
39947
+ const { capabilities } = await this.request('initialize', this.getInitializeParams(), timeout * 3);
39925
39948
  this.capabilities = capabilities;
39926
39949
  this.notify('initialized', {});
39927
39950
  this.ready = true;
@@ -39941,6 +39964,9 @@ class LanguageServerClient {
39941
39964
  async textDocumentCompletion(params) {
39942
39965
  return await this.request('textDocument/completion', params, timeout);
39943
39966
  }
39967
+ async textDocumentDefinition(params) {
39968
+ return await this.request('textDocument/definition', params, timeout);
39969
+ }
39944
39970
  attachPlugin(plugin) {
39945
39971
  this.plugins.push(plugin);
39946
39972
  }
@@ -40174,6 +40200,35 @@ class LanguageServerPlugin {
40174
40200
  filter: false,
40175
40201
  };
40176
40202
  }
40203
+ async requestDefinition(view, { line, character }) {
40204
+ if (!this.client.ready ||
40205
+ !this.client.capabilities.definitionProvider) {
40206
+ return null;
40207
+ }
40208
+ const result = await this.client.textDocumentDefinition({
40209
+ textDocument: { uri: this.documentUri },
40210
+ position: { line, character },
40211
+ });
40212
+ if (!result)
40213
+ return;
40214
+ const location = Array.isArray(result) ? result[0] : result;
40215
+ const uri = location.uri ||
40216
+ location.targetUri;
40217
+ const range = location.range ||
40218
+ location.targetRange;
40219
+ if (uri === this.documentUri) {
40220
+ view.dispatch(view.state.update({
40221
+ selection: {
40222
+ anchor: posToOffset(view.state.doc, range.start),
40223
+ head: posToOffset(view.state.doc, range.end),
40224
+ },
40225
+ }));
40226
+ }
40227
+ return {
40228
+ uri,
40229
+ range,
40230
+ };
40231
+ }
40177
40232
  processNotification(notification) {
40178
40233
  try {
40179
40234
  switch (notification.method) {
@@ -40217,34 +40272,38 @@ class LanguageServerPlugin {
40217
40272
  this.view.dispatch(setDiagnostics(this.view.state, diagnostics));
40218
40273
  }
40219
40274
  }
40275
+ const languageServerPlugin = ViewPlugin.fromClass(LanguageServerPlugin);
40220
40276
  function languageServer(options) {
40221
40277
  const serverUri = options.serverUri;
40222
- delete options.serverUri;
40278
+ const { serverUri: _, ...optionsWithoutServerUri } = options;
40223
40279
  return languageServerWithTransport({
40224
- ...options,
40280
+ ...optionsWithoutServerUri,
40225
40281
  transport: new buildExports.WebSocketTransport(serverUri),
40226
40282
  });
40227
40283
  }
40228
40284
  function languageServerWithTransport(options) {
40229
- let plugin = null;
40230
40285
  return [
40231
40286
  client.of(options.client ||
40232
- new LanguageServerClient({ ...options, autoClose: true })),
40287
+ new LanguageServerClient({
40288
+ ...options,
40289
+ autoClose: true,
40290
+ })),
40233
40291
  documentUri.of(options.documentUri),
40234
40292
  languageId.of(options.languageId),
40235
- ViewPlugin.define((view) => (plugin = new LanguageServerPlugin(view, options.allowHTMLContent))),
40293
+ languageServerPlugin.of(options.allowHTMLContent),
40236
40294
  hoverTooltip((view, pos) => {
40237
40295
  var _a;
40238
- return (_a = plugin === null || plugin === void 0 ? void 0 : plugin.requestHoverTooltip(view, offsetToPos(view.state.doc, pos))) !== null && _a !== void 0 ? _a : null;
40296
+ const plugin = view.plugin(languageServerPlugin);
40297
+ return ((_a = plugin === null || plugin === void 0 ? void 0 : plugin.requestHoverTooltip(view, offsetToPos(view.state.doc, pos))) !== null && _a !== void 0 ? _a : null);
40239
40298
  }),
40240
40299
  autocompletion({
40241
40300
  override: [
40242
40301
  async (context) => {
40243
40302
  var _a, _b, _c;
40244
- if (plugin == null) {
40303
+ const { state, pos, explicit, view } = context;
40304
+ const plugin = view.plugin(languageServerPlugin);
40305
+ if (plugin == null)
40245
40306
  return null;
40246
- }
40247
- const { state, pos, explicit } = context;
40248
40307
  const line = state.doc.lineAt(pos);
40249
40308
  let trigKind = mainExports.CompletionTriggerKind.Invoked;
40250
40309
  let trigChar;
@@ -40266,23 +40325,6 @@ function languageServerWithTransport(options) {
40266
40325
  }),
40267
40326
  ];
40268
40327
  }
40269
- function posToOffset(doc, pos) {
40270
- if (pos.line >= doc.lines) {
40271
- return;
40272
- }
40273
- const offset = doc.line(pos.line + 1).from + pos.character;
40274
- if (offset > doc.length) {
40275
- return;
40276
- }
40277
- return offset;
40278
- }
40279
- function offsetToPos(doc, offset) {
40280
- const line = doc.lineAt(offset);
40281
- return {
40282
- character: offset - line.from,
40283
- line: line.number - 1,
40284
- };
40285
- }
40286
40328
  async function formatContents(contents) {
40287
40329
  if (isLSPMarkupContent(contents)) {
40288
40330
  let value = contents.value;
@@ -40313,10 +40355,11 @@ function prefixMatch(items) {
40313
40355
  const first = new Set();
40314
40356
  const rest = new Set();
40315
40357
  for (const item of items) {
40316
- const [initial, ...restStr] = ((_a = item.textEdit) === null || _a === void 0 ? void 0 : _a.newText) || item.label;
40358
+ const text = ((_a = item.textEdit) === null || _a === void 0 ? void 0 : _a.newText) || item.label;
40359
+ const initial = text[0];
40317
40360
  first.add(initial);
40318
- for (const char of restStr) {
40319
- rest.add(char);
40361
+ for (let i = 1; i < text.length; i++) {
40362
+ rest.add(text[i]);
40320
40363
  }
40321
40364
  }
40322
40365
  const source = toSet(first) + toSet(rest) + '*$';
@@ -40329,6 +40372,19 @@ function isLSPMarkupContent(contents) {
40329
40372
  return contents.kind !== undefined;
40330
40373
  }
40331
40374
 
40375
+ function jumpTo(view, pos) {
40376
+ const plugin = view.plugin(languageServerPlugin);
40377
+ if (!plugin)
40378
+ return false;
40379
+ plugin.requestDefinition(view, offsetToPos(view.state.doc, pos));
40380
+ return true;
40381
+ }
40382
+ const jumpToDefinition = (view) => jumpTo(view, view.state.selection.main.head);
40383
+ const jumpToDefinitionPos = (pos) => (view) => jumpTo(view, pos);
40384
+ const jumpToDefinitionKeymap = [
40385
+ { key: 'F12', run: jumpToDefinition, preventDefault: true },
40386
+ ];
40387
+
40332
40388
  var tabSizeCompartment = new Compartment();
40333
40389
  new EditorView({
40334
40390
  doc: '#include <iostream>\n\nusing namespace std;\n\nint main() {\n\treturn 0;\n}\n',
@@ -40345,5 +40401,15 @@ new EditorView({
40345
40401
  }],
40346
40402
  documentUri: "file:///demo/main.cpp",
40347
40403
  languageId: 'cpp'
40404
+ }), keymap.of(_toConsumableArray(jumpToDefinitionKeymap)), EditorView.domEventHandlers({
40405
+ click: function click(event, view) {
40406
+ if (!event.ctrlKey && !event.metaKey) return;
40407
+ var pos = view.posAtCoords({
40408
+ x: event.clientX,
40409
+ y: event.clientY
40410
+ });
40411
+ var ok = jumpToDefinitionPos(pos)(view);
40412
+ if (ok) event.preventDefault();
40413
+ }
40348
40414
  })]
40349
40415
  });
@@ -0,0 +1,4 @@
1
+ import { Command, KeyBinding } from '@codemirror/view';
2
+ export declare const jumpToDefinition: Command;
3
+ export declare const jumpToDefinitionPos: (pos: number) => Command;
4
+ export declare const jumpToDefinitionKeymap: readonly KeyBinding[];
package/dist/index.d.ts CHANGED
@@ -1,98 +1,2 @@
1
- import { EditorView, Tooltip } from '@codemirror/view';
2
- import { CompletionTriggerKind } from 'vscode-languageserver-protocol';
3
- import type { CompletionContext, CompletionResult } from '@codemirror/autocomplete';
4
- import type { PluginValue, ViewUpdate } from '@codemirror/view';
5
- import { Transport } from '@open-rpc/client-js/build/transports/Transport';
6
- import type { PublishDiagnosticsParams } from 'vscode-languageserver-protocol';
7
- import type * as LSP from 'vscode-languageserver-protocol';
8
- interface LSPEventMap {
9
- 'textDocument/publishDiagnostics': LSP.PublishDiagnosticsParams;
10
- }
11
- type Notification = {
12
- [key in keyof LSPEventMap]: {
13
- jsonrpc: '2.0';
14
- id?: null | undefined;
15
- method: key;
16
- params: LSPEventMap[key];
17
- };
18
- }[keyof LSPEventMap];
19
- export declare class LanguageServerClient {
20
- ready: boolean;
21
- capabilities: LSP.ServerCapabilities<any>;
22
- initializePromise: Promise<void>;
23
- private rootUri;
24
- private workspaceFolders;
25
- private autoClose?;
26
- private transport;
27
- private requestManager;
28
- private client;
29
- private plugins;
30
- constructor(options: LanguageServerClientOptions);
31
- initialize(): Promise<void>;
32
- close(): void;
33
- textDocumentDidOpen(params: LSP.DidOpenTextDocumentParams): Promise<LSP.DidOpenTextDocumentParams>;
34
- textDocumentDidChange(params: LSP.DidChangeTextDocumentParams): Promise<LSP.DidChangeTextDocumentParams>;
35
- textDocumentHover(params: LSP.HoverParams): Promise<LSP.Hover>;
36
- textDocumentCompletion(params: LSP.CompletionParams): Promise<LSP.CompletionList | LSP.CompletionItem[]>;
37
- attachPlugin(plugin: LanguageServerPlugin): void;
38
- detachPlugin(plugin: LanguageServerPlugin): void;
39
- private request;
40
- private notify;
41
- private processNotification;
42
- }
43
- declare class LanguageServerPlugin implements PluginValue {
44
- private view;
45
- private allowHTMLContent;
46
- client: LanguageServerClient;
47
- private documentUri;
48
- private languageId;
49
- private documentVersion;
50
- private changesTimeout;
51
- constructor(view: EditorView, allowHTMLContent: boolean);
52
- update({ docChanged }: ViewUpdate): void;
53
- destroy(): void;
54
- initialize({ documentText }: {
55
- documentText: string;
56
- }): Promise<void>;
57
- sendChange({ documentText }: {
58
- documentText: string;
59
- }): Promise<void>;
60
- requestDiagnostics(view: EditorView): void;
61
- requestHoverTooltip(view: EditorView, { line, character }: {
62
- line: number;
63
- character: number;
64
- }): Promise<Tooltip | null>;
65
- requestCompletion(context: CompletionContext, { line, character }: {
66
- line: number;
67
- character: number;
68
- }, { triggerKind, triggerCharacter, }: {
69
- triggerKind: CompletionTriggerKind;
70
- triggerCharacter: string | undefined;
71
- }): Promise<CompletionResult | null>;
72
- processNotification(notification: Notification): void;
73
- processDiagnostics(params: PublishDiagnosticsParams): void;
74
- }
75
- interface LanguageServerBaseOptions {
76
- rootUri: string | null;
77
- workspaceFolders: LSP.WorkspaceFolder[] | null;
78
- documentUri: string;
79
- languageId: string;
80
- }
81
- interface LanguageServerClientOptions extends LanguageServerBaseOptions {
82
- transport: Transport;
83
- autoClose?: boolean;
84
- }
85
- interface LanguageServerOptions extends LanguageServerClientOptions {
86
- client?: LanguageServerClient;
87
- allowHTMLContent?: boolean;
88
- }
89
- interface LanguageServerWebsocketOptions extends LanguageServerBaseOptions {
90
- serverUri: `ws://${string}` | `wss://${string}`;
91
- }
92
- export declare function languageServer(options: LanguageServerWebsocketOptions): ({
93
- extension: import("@codemirror/state").Extension;
94
- } | readonly import("@codemirror/state").Extension[])[];
95
- export declare function languageServerWithTransport(options: LanguageServerOptions): ({
96
- extension: import("@codemirror/state").Extension;
97
- } | readonly import("@codemirror/state").Extension[])[];
98
- export {};
1
+ export { LanguageServerClient, languageServerPlugin, languageServer, languageServerWithTransport, } from './plugin';
2
+ export { jumpToDefinition, jumpToDefinitionPos, jumpToDefinitionKeymap, } from './definition';
package/dist/index.js CHANGED
@@ -1,11 +1,29 @@
1
- import { autocompletion, insertCompletionText } from '@codemirror/autocomplete';
1
+ import { insertCompletionText, autocompletion } from '@codemirror/autocomplete';
2
2
  import { setDiagnostics } from '@codemirror/lint';
3
3
  import { Facet } from '@codemirror/state';
4
4
  import { ViewPlugin, hoverTooltip } from '@codemirror/view';
5
5
  import { RequestManager, Client, WebSocketTransport } from '@open-rpc/client-js';
6
- import { CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity } from 'vscode-languageserver-protocol';
6
+ import { CompletionItemKind, DiagnosticSeverity, CompletionTriggerKind } from 'vscode-languageserver-protocol';
7
7
  import { marked } from 'marked';
8
8
 
9
+ function posToOffset(doc, pos) {
10
+ if (pos.line >= doc.lines) {
11
+ return;
12
+ }
13
+ const offset = doc.line(pos.line + 1).from + pos.character;
14
+ if (offset > doc.length) {
15
+ return;
16
+ }
17
+ return offset;
18
+ }
19
+ function offsetToPos(doc, offset) {
20
+ const line = doc.lineAt(offset);
21
+ return {
22
+ character: offset - line.from,
23
+ line: line.number - 1,
24
+ };
25
+ }
26
+
9
27
  const timeout = 10000;
10
28
  const changesDelay = 500;
11
29
  const CompletionItemKindMap = Object.fromEntries(Object.entries(CompletionItemKind).map(([key, value]) => [value, key]));
@@ -17,6 +35,7 @@ const documentUri = Facet.define({ combine: useLast });
17
35
  const languageId = Facet.define({ combine: useLast });
18
36
  class LanguageServerClient {
19
37
  constructor(options) {
38
+ this.options = options;
20
39
  this.rootUri = options.rootUri;
21
40
  this.workspaceFolders = options.workspaceFolders;
22
41
  this.autoClose = options.autoClose;
@@ -44,8 +63,9 @@ class LanguageServerClient {
44
63
  }
45
64
  this.initializePromise = this.initialize();
46
65
  }
47
- async initialize() {
48
- const { capabilities } = await this.request('initialize', {
66
+ getInitializeParams() {
67
+ var _a;
68
+ return {
49
69
  capabilities: {
50
70
  textDocument: {
51
71
  hover: {
@@ -68,7 +88,7 @@ class LanguageServerClient {
68
88
  deprecatedSupport: false,
69
89
  preselectSupport: false,
70
90
  },
71
- contextSupport: false,
91
+ contextSupport: true,
72
92
  },
73
93
  signatureHelp: {
74
94
  dynamicRegistration: true,
@@ -99,11 +119,14 @@ class LanguageServerClient {
99
119
  },
100
120
  },
101
121
  },
102
- initializationOptions: null,
122
+ initializationOptions: (_a = this.options.initializationOptions) !== null && _a !== void 0 ? _a : null,
103
123
  processId: null,
104
124
  rootUri: this.rootUri,
105
125
  workspaceFolders: this.workspaceFolders,
106
- }, timeout * 3);
126
+ };
127
+ }
128
+ async initialize() {
129
+ const { capabilities } = await this.request('initialize', this.getInitializeParams(), timeout * 3);
107
130
  this.capabilities = capabilities;
108
131
  this.notify('initialized', {});
109
132
  this.ready = true;
@@ -123,6 +146,9 @@ class LanguageServerClient {
123
146
  async textDocumentCompletion(params) {
124
147
  return await this.request('textDocument/completion', params, timeout);
125
148
  }
149
+ async textDocumentDefinition(params) {
150
+ return await this.request('textDocument/definition', params, timeout);
151
+ }
126
152
  attachPlugin(plugin) {
127
153
  this.plugins.push(plugin);
128
154
  }
@@ -356,6 +382,35 @@ class LanguageServerPlugin {
356
382
  filter: false,
357
383
  };
358
384
  }
385
+ async requestDefinition(view, { line, character }) {
386
+ if (!this.client.ready ||
387
+ !this.client.capabilities.definitionProvider) {
388
+ return null;
389
+ }
390
+ const result = await this.client.textDocumentDefinition({
391
+ textDocument: { uri: this.documentUri },
392
+ position: { line, character },
393
+ });
394
+ if (!result)
395
+ return;
396
+ const location = Array.isArray(result) ? result[0] : result;
397
+ const uri = location.uri ||
398
+ location.targetUri;
399
+ const range = location.range ||
400
+ location.targetRange;
401
+ if (uri === this.documentUri) {
402
+ view.dispatch(view.state.update({
403
+ selection: {
404
+ anchor: posToOffset(view.state.doc, range.start),
405
+ head: posToOffset(view.state.doc, range.end),
406
+ },
407
+ }));
408
+ }
409
+ return {
410
+ uri,
411
+ range,
412
+ };
413
+ }
359
414
  processNotification(notification) {
360
415
  try {
361
416
  switch (notification.method) {
@@ -399,34 +454,38 @@ class LanguageServerPlugin {
399
454
  this.view.dispatch(setDiagnostics(this.view.state, diagnostics));
400
455
  }
401
456
  }
457
+ const languageServerPlugin = ViewPlugin.fromClass(LanguageServerPlugin);
402
458
  function languageServer(options) {
403
459
  const serverUri = options.serverUri;
404
- delete options.serverUri;
460
+ const { serverUri: _, ...optionsWithoutServerUri } = options;
405
461
  return languageServerWithTransport({
406
- ...options,
462
+ ...optionsWithoutServerUri,
407
463
  transport: new WebSocketTransport(serverUri),
408
464
  });
409
465
  }
410
466
  function languageServerWithTransport(options) {
411
- let plugin = null;
412
467
  return [
413
468
  client.of(options.client ||
414
- new LanguageServerClient({ ...options, autoClose: true })),
469
+ new LanguageServerClient({
470
+ ...options,
471
+ autoClose: true,
472
+ })),
415
473
  documentUri.of(options.documentUri),
416
474
  languageId.of(options.languageId),
417
- ViewPlugin.define((view) => (plugin = new LanguageServerPlugin(view, options.allowHTMLContent))),
475
+ languageServerPlugin.of(options.allowHTMLContent),
418
476
  hoverTooltip((view, pos) => {
419
477
  var _a;
420
- return (_a = plugin === null || plugin === void 0 ? void 0 : plugin.requestHoverTooltip(view, offsetToPos(view.state.doc, pos))) !== null && _a !== void 0 ? _a : null;
478
+ const plugin = view.plugin(languageServerPlugin);
479
+ return ((_a = plugin === null || plugin === void 0 ? void 0 : plugin.requestHoverTooltip(view, offsetToPos(view.state.doc, pos))) !== null && _a !== void 0 ? _a : null);
421
480
  }),
422
481
  autocompletion({
423
482
  override: [
424
483
  async (context) => {
425
484
  var _a, _b, _c;
426
- if (plugin == null) {
485
+ const { state, pos, explicit, view } = context;
486
+ const plugin = view.plugin(languageServerPlugin);
487
+ if (plugin == null)
427
488
  return null;
428
- }
429
- const { state, pos, explicit } = context;
430
489
  const line = state.doc.lineAt(pos);
431
490
  let trigKind = CompletionTriggerKind.Invoked;
432
491
  let trigChar;
@@ -448,23 +507,6 @@ function languageServerWithTransport(options) {
448
507
  }),
449
508
  ];
450
509
  }
451
- function posToOffset(doc, pos) {
452
- if (pos.line >= doc.lines) {
453
- return;
454
- }
455
- const offset = doc.line(pos.line + 1).from + pos.character;
456
- if (offset > doc.length) {
457
- return;
458
- }
459
- return offset;
460
- }
461
- function offsetToPos(doc, offset) {
462
- const line = doc.lineAt(offset);
463
- return {
464
- character: offset - line.from,
465
- line: line.number - 1,
466
- };
467
- }
468
510
  async function formatContents(contents) {
469
511
  if (isLSPMarkupContent(contents)) {
470
512
  let value = contents.value;
@@ -495,10 +537,11 @@ function prefixMatch(items) {
495
537
  const first = new Set();
496
538
  const rest = new Set();
497
539
  for (const item of items) {
498
- const [initial, ...restStr] = ((_a = item.textEdit) === null || _a === void 0 ? void 0 : _a.newText) || item.label;
540
+ const text = ((_a = item.textEdit) === null || _a === void 0 ? void 0 : _a.newText) || item.label;
541
+ const initial = text[0];
499
542
  first.add(initial);
500
- for (const char of restStr) {
501
- rest.add(char);
543
+ for (let i = 1; i < text.length; i++) {
544
+ rest.add(text[i]);
502
545
  }
503
546
  }
504
547
  const source = toSet(first) + toSet(rest) + '*$';
@@ -511,4 +554,17 @@ function isLSPMarkupContent(contents) {
511
554
  return contents.kind !== undefined;
512
555
  }
513
556
 
514
- export { LanguageServerClient, languageServer, languageServerWithTransport };
557
+ function jumpTo(view, pos) {
558
+ const plugin = view.plugin(languageServerPlugin);
559
+ if (!plugin)
560
+ return false;
561
+ plugin.requestDefinition(view, offsetToPos(view.state.doc, pos));
562
+ return true;
563
+ }
564
+ const jumpToDefinition = (view) => jumpTo(view, view.state.selection.main.head);
565
+ const jumpToDefinitionPos = (pos) => (view) => jumpTo(view, pos);
566
+ const jumpToDefinitionKeymap = [
567
+ { key: 'F12', run: jumpToDefinition, preventDefault: true },
568
+ ];
569
+
570
+ export { LanguageServerClient, jumpToDefinition, jumpToDefinitionKeymap, jumpToDefinitionPos, languageServer, languageServerPlugin, languageServerWithTransport };
@@ -0,0 +1,407 @@
1
+ import { EditorView, Tooltip, ViewPlugin } from '@codemirror/view';
2
+ import { CompletionTriggerKind } from 'vscode-languageserver-protocol';
3
+ import type { CompletionContext, CompletionResult } from '@codemirror/autocomplete';
4
+ import type { PluginValue, ViewUpdate } from '@codemirror/view';
5
+ import { Transport } from '@open-rpc/client-js/build/transports/Transport';
6
+ import type { PublishDiagnosticsParams } from 'vscode-languageserver-protocol';
7
+ import type * as LSP from 'vscode-languageserver-protocol';
8
+ interface LSPRequestMap {
9
+ initialize: [LSP.InitializeParams, LSP.InitializeResult];
10
+ 'textDocument/hover': [LSP.HoverParams, LSP.Hover];
11
+ 'textDocument/completion': [
12
+ LSP.CompletionParams,
13
+ LSP.CompletionItem[] | LSP.CompletionList | null
14
+ ];
15
+ 'textDocument/definition': [
16
+ LSP.DefinitionParams,
17
+ LSP.Location | LSP.Location[] | LSP.LocationLink[] | null
18
+ ];
19
+ }
20
+ interface LSPNotifyMap {
21
+ initialized: LSP.InitializedParams;
22
+ 'textDocument/didChange': LSP.DidChangeTextDocumentParams;
23
+ 'textDocument/didOpen': LSP.DidOpenTextDocumentParams;
24
+ }
25
+ interface LSPEventMap {
26
+ 'textDocument/publishDiagnostics': LSP.PublishDiagnosticsParams;
27
+ }
28
+ type Notification = {
29
+ [key in keyof LSPEventMap]: {
30
+ jsonrpc: '2.0';
31
+ id?: null | undefined;
32
+ method: key;
33
+ params: LSPEventMap[key];
34
+ };
35
+ }[keyof LSPEventMap];
36
+ export declare class LanguageServerClient<InitializationOptions = unknown> {
37
+ ready: boolean;
38
+ capabilities: LSP.ServerCapabilities<any>;
39
+ initializePromise: Promise<void>;
40
+ private rootUri;
41
+ private workspaceFolders;
42
+ private autoClose?;
43
+ private transport;
44
+ private requestManager;
45
+ private client;
46
+ private plugins;
47
+ private options;
48
+ constructor(options: LanguageServerClientOptions<InitializationOptions>);
49
+ protected getInitializeParams(): LSP.InitializeParams;
50
+ initialize(): Promise<void>;
51
+ close(): void;
52
+ textDocumentDidOpen(params: LSP.DidOpenTextDocumentParams): Promise<LSP.DidOpenTextDocumentParams>;
53
+ textDocumentDidChange(params: LSP.DidChangeTextDocumentParams): Promise<LSP.DidChangeTextDocumentParams>;
54
+ textDocumentHover(params: LSP.HoverParams): Promise<LSP.Hover>;
55
+ textDocumentCompletion(params: LSP.CompletionParams): Promise<LSP.CompletionList | LSP.CompletionItem[]>;
56
+ textDocumentDefinition(params: LSP.DefinitionParams): Promise<LSP.Location | LSP.Location[] | LSP.LocationLink[]>;
57
+ attachPlugin(plugin: LanguageServerPlugin): void;
58
+ detachPlugin(plugin: LanguageServerPlugin): void;
59
+ protected request<K extends keyof LSPRequestMap>(method: K, params: LSPRequestMap[K][0], timeout: number): Promise<LSPRequestMap[K][1]>;
60
+ protected notify<K extends keyof LSPNotifyMap>(method: K, params: LSPNotifyMap[K]): Promise<LSPNotifyMap[K]>;
61
+ protected processNotification(notification: Notification): void;
62
+ }
63
+ declare class LanguageServerPlugin implements PluginValue {
64
+ private view;
65
+ private allowHTMLContent;
66
+ client: LanguageServerClient;
67
+ private documentUri;
68
+ private languageId;
69
+ private documentVersion;
70
+ private changesTimeout;
71
+ constructor(view: EditorView, allowHTMLContent: boolean);
72
+ update({ docChanged }: ViewUpdate): void;
73
+ destroy(): void;
74
+ initialize({ documentText }: {
75
+ documentText: string;
76
+ }): Promise<void>;
77
+ sendChange({ documentText }: {
78
+ documentText: string;
79
+ }): Promise<void>;
80
+ requestDiagnostics(view: EditorView): void;
81
+ requestHoverTooltip(view: EditorView, { line, character }: {
82
+ line: number;
83
+ character: number;
84
+ }): Promise<Tooltip | null>;
85
+ requestCompletion(context: CompletionContext, { line, character }: {
86
+ line: number;
87
+ character: number;
88
+ }, { triggerKind, triggerCharacter, }: {
89
+ triggerKind: CompletionTriggerKind;
90
+ triggerCharacter: string | undefined;
91
+ }): Promise<CompletionResult | null>;
92
+ requestDefinition(view: EditorView, { line, character }: {
93
+ line: number;
94
+ character: number;
95
+ }): Promise<{
96
+ uri: string;
97
+ range: LSP.Range;
98
+ }>;
99
+ processNotification(notification: Notification): void;
100
+ processDiagnostics(params: PublishDiagnosticsParams): void;
101
+ }
102
+ export declare const languageServerPlugin: ViewPlugin<LanguageServerPlugin, boolean>;
103
+ interface LanguageServerBaseOptions {
104
+ rootUri: string | null;
105
+ workspaceFolders: LSP.WorkspaceFolder[] | null;
106
+ documentUri: string;
107
+ languageId: string;
108
+ }
109
+ interface LanguageServerClientOptions<InitializationOptions = unknown> extends LanguageServerBaseOptions {
110
+ transport: Transport;
111
+ autoClose?: boolean;
112
+ initializationOptions?: InitializationOptions;
113
+ }
114
+ interface LanguageServerOptions<InitializationOptions = unknown> extends LanguageServerClientOptions<InitializationOptions> {
115
+ client?: LanguageServerClient<InitializationOptions>;
116
+ allowHTMLContent?: boolean;
117
+ }
118
+ interface LanguageServerWebsocketOptions<InitializationOptions = unknown> extends LanguageServerBaseOptions {
119
+ serverUri: `ws://${string}` | `wss://${string}`;
120
+ initializationOptions?: InitializationOptions;
121
+ }
122
+ export declare function languageServer<InitializationOptions = unknown>(options: LanguageServerWebsocketOptions<InitializationOptions>): ({
123
+ extension: import("@codemirror/state").Extension;
124
+ } | readonly import("@codemirror/state").Extension[])[];
125
+ export declare function languageServerWithTransport<InitializationOptions = unknown>(options: LanguageServerOptions<InitializationOptions>): ({
126
+ extension: import("@codemirror/state").Extension;
127
+ } | readonly import("@codemirror/state").Extension[])[];
128
+ /**
129
+ * Initialization options for Pyright language server
130
+ * @see https://github.com/microsoft/pyright/blob/main/docs/settings.md
131
+ */
132
+ export interface PyrightInitializationOptions {
133
+ python?: {
134
+ pythonPath?: string;
135
+ venvPath?: string;
136
+ analysis?: {
137
+ autoSearchPaths?: boolean;
138
+ extraPaths?: string[];
139
+ diagnosticMode?: 'workspace' | 'openFilesOnly';
140
+ stubPath?: string;
141
+ typeshedPaths?: string[];
142
+ useLibraryCodeForTypes?: boolean;
143
+ typeCheckingMode?: 'off' | 'basic' | 'strict';
144
+ autoImportCompletions?: boolean;
145
+ indexing?: boolean;
146
+ };
147
+ };
148
+ reportMissingImports?: boolean;
149
+ reportMissingTypeStubs?: boolean;
150
+ reportMissingModuleSource?: boolean;
151
+ reportInvalidTypeVarUse?: boolean;
152
+ reportOptionalSubscript?: boolean;
153
+ reportOptionalMemberAccess?: boolean;
154
+ reportOptionalCall?: boolean;
155
+ reportOptionalIterable?: boolean;
156
+ reportOptionalContextManager?: boolean;
157
+ reportOptionalOperand?: boolean;
158
+ reportTypedDictNotRequiredAccess?: boolean;
159
+ reportPrivateImportUsage?: boolean;
160
+ reportConstantRedefinition?: boolean;
161
+ reportIncompatibleMethodOverride?: boolean;
162
+ reportIncompatibleVariableOverride?: boolean;
163
+ reportInconsistentConstructor?: boolean;
164
+ }
165
+ /**
166
+ * Initialization options for Rust Analyzer language server
167
+ * @see https://rust-analyzer.github.io/manual.html#configuration
168
+ */
169
+ export interface RustAnalyzerInitializationOptions {
170
+ cargo?: {
171
+ buildScripts?: {
172
+ enable?: boolean;
173
+ invocationStrategy?: 'per_workspace' | 'once';
174
+ invocationLocation?: 'workspace' | 'root';
175
+ };
176
+ allTargets?: boolean;
177
+ noDefaultFeatures?: boolean;
178
+ allFeatures?: boolean;
179
+ features?: string[];
180
+ target?: string;
181
+ runBuildScripts?: boolean;
182
+ useRustcWrapperForBuildScripts?: boolean;
183
+ };
184
+ procMacro?: {
185
+ enable?: boolean;
186
+ ignored?: Record<string, string[]>;
187
+ server?: string;
188
+ attributes?: {
189
+ enable?: boolean;
190
+ };
191
+ };
192
+ diagnostics?: {
193
+ enable?: boolean;
194
+ disabled?: string[];
195
+ warningsAsHint?: string[];
196
+ warningsAsInfo?: string[];
197
+ remapPrefix?: Record<string, string>;
198
+ experimental?: {
199
+ enable?: boolean;
200
+ };
201
+ };
202
+ completion?: {
203
+ addCallArgumentSnippets?: boolean;
204
+ addCallParenthesis?: boolean;
205
+ postfix?: {
206
+ enable?: boolean;
207
+ };
208
+ autoimport?: {
209
+ enable?: boolean;
210
+ };
211
+ privateEditable?: {
212
+ enable?: boolean;
213
+ };
214
+ };
215
+ assist?: {
216
+ importGranularity?: 'preserve' | 'crate' | 'module' | 'item';
217
+ importEnforceGranularity?: boolean;
218
+ importPrefix?: 'plain' | 'by_self' | 'by_crate';
219
+ allowMergingIntoGlobImports?: boolean;
220
+ };
221
+ callInfo?: {
222
+ full?: boolean;
223
+ };
224
+ lens?: {
225
+ enable?: boolean;
226
+ run?: boolean;
227
+ debug?: boolean;
228
+ implementations?: boolean;
229
+ refs?: boolean;
230
+ methodReferences?: boolean;
231
+ references?: boolean;
232
+ enumVariantReferences?: boolean;
233
+ };
234
+ hover?: {
235
+ documentation?: boolean;
236
+ keywords?: boolean;
237
+ linksInHover?: boolean;
238
+ memoryLayout?: {
239
+ enable?: boolean;
240
+ };
241
+ };
242
+ workspace?: {
243
+ symbol?: {
244
+ search?: {
245
+ scope?: 'workspace' | 'workspace_and_dependencies';
246
+ kind?: 'only_types' | 'all_symbols';
247
+ };
248
+ };
249
+ };
250
+ }
251
+ /**
252
+ * Initialization options for TypeScript/JavaScript language server
253
+ * @see https://github.com/typescript-language-server/typescript-language-server
254
+ */
255
+ export interface TypeScriptInitializationOptions {
256
+ hostInfo?: string;
257
+ npmLocation?: string;
258
+ globalPlugins?: string[];
259
+ pluginProbeLocations?: string[];
260
+ preferences?: {
261
+ includePackageJsonAutoImports?: 'auto' | 'on' | 'off';
262
+ providePrefixAndSuffixTextForRename?: boolean;
263
+ allowRenameOfImportPath?: boolean;
264
+ includeAutomaticOptionalChainCompletions?: boolean;
265
+ includeCompletionsForModuleExports?: boolean;
266
+ includeCompletionsForImportStatements?: boolean;
267
+ includeCompletionsWithSnippetText?: boolean;
268
+ includeCompletionsWithInsertText?: boolean;
269
+ allowIncompleteCompletions?: boolean;
270
+ importModuleSpecifier?: 'shortest' | 'relative' | 'absolute' | 'auto';
271
+ importModuleSpecifierEnding?: 'minimal' | 'index' | 'js';
272
+ allowTextChangesInNewFiles?: boolean;
273
+ lazyConfiguredProjectsFromExternalProject?: boolean;
274
+ providePrefixAndSuffixTextForQuickInfo?: boolean;
275
+ includeInlayParameterNameHints?: 'none' | 'literals' | 'all';
276
+ includeInlayParameterNameHintsWhenArgumentMatchesName?: boolean;
277
+ includeInlayFunctionParameterTypeHints?: boolean;
278
+ includeInlayVariableTypeHints?: boolean;
279
+ includeInlayVariableTypeHintsWhenTypeMatchesName?: boolean;
280
+ includeInlayPropertyDeclarationTypeHints?: boolean;
281
+ includeInlayFunctionLikeReturnTypeHints?: boolean;
282
+ includeInlayEnumMemberValueHints?: boolean;
283
+ };
284
+ locale?: string;
285
+ maxTsServerMemory?: number;
286
+ tsserver?: {
287
+ logLevel?: 'off' | 'terse' | 'normal' | 'requestTime' | 'verbose';
288
+ logVerbosity?: 'off' | 'terse' | 'normal' | 'requestTime' | 'verbose';
289
+ trace?: 'off' | 'messages' | 'verbose';
290
+ useSeparateSyntaxServer?: boolean;
291
+ enableTracing?: boolean;
292
+ path?: string;
293
+ };
294
+ }
295
+ /**
296
+ * Initialization options for ESLint language server
297
+ * @see https://github.com/Microsoft/vscode-eslint
298
+ */
299
+ export interface ESLintInitializationOptions {
300
+ packageManager?: 'npm' | 'yarn' | 'pnpm';
301
+ nodePath?: string;
302
+ options?: Record<string, any>;
303
+ rules?: Record<string, any>;
304
+ rulesCustomizations?: Array<{
305
+ rule: string;
306
+ severity: 'downgrade' | 'upgrade' | 'info' | 'warn' | 'error' | 'off';
307
+ }>;
308
+ run?: 'onType' | 'onSave';
309
+ problems?: {
310
+ shortenToSingleLine?: boolean;
311
+ };
312
+ codeAction?: {
313
+ disableRuleComment?: {
314
+ enable?: boolean;
315
+ location?: 'separateLine' | 'sameLine';
316
+ };
317
+ showDocumentation?: {
318
+ enable?: boolean;
319
+ };
320
+ };
321
+ codeActionOnSave?: {
322
+ enable?: boolean;
323
+ mode?: 'all' | 'problems';
324
+ };
325
+ format?: {
326
+ enable?: boolean;
327
+ };
328
+ quiet?: boolean;
329
+ onIgnoredFiles?: 'off' | 'warn';
330
+ useESLintClass?: boolean;
331
+ experimental?: {
332
+ useFlatConfig?: boolean;
333
+ };
334
+ workingDirectory?: {
335
+ mode?: 'auto' | 'location';
336
+ };
337
+ }
338
+ /**
339
+ * Initialization options for Clangd language server
340
+ * @see https://clangd.llvm.org/config
341
+ */
342
+ export interface ClangdInitializationOptions {
343
+ compilationDatabasePath?: string;
344
+ compilationDatabaseChanges?: Record<string, any>;
345
+ fallbackFlags?: string[];
346
+ clangdFileStatus?: boolean;
347
+ utf8?: boolean;
348
+ offsetEncoding?: ('utf-8' | 'utf-16' | 'utf-32')[];
349
+ index?: {
350
+ background?: 'Build' | 'Skip';
351
+ threads?: number;
352
+ };
353
+ completion?: {
354
+ detailedLabel?: boolean;
355
+ allScopes?: boolean;
356
+ };
357
+ hover?: {
358
+ showAKA?: boolean;
359
+ };
360
+ inlayHints?: {
361
+ enabled?: boolean;
362
+ parameterNames?: boolean;
363
+ deducedTypes?: boolean;
364
+ designators?: boolean;
365
+ };
366
+ semanticHighlighting?: boolean;
367
+ diagnostics?: {
368
+ unusedIncludes?: 'None' | 'Strict';
369
+ missingIncludes?: 'None' | 'Strict';
370
+ clangTidy?: boolean;
371
+ suppressAll?: boolean;
372
+ };
373
+ }
374
+ /**
375
+ * Initialization options for Gopls (Go language server)
376
+ * @see https://github.com/golang/tools/blob/master/gopls/doc/settings.md
377
+ */
378
+ export interface GoplsInitializationOptions {
379
+ buildFlags?: string[];
380
+ env?: Record<string, string>;
381
+ directoryFilters?: string[];
382
+ templateExtensions?: string[];
383
+ memoryMode?: 'DegradeClosed' | 'Normal';
384
+ gofumpt?: boolean;
385
+ staticcheck?: boolean;
386
+ analyses?: Record<string, boolean>;
387
+ codelenses?: Record<string, boolean>;
388
+ usePlaceholders?: boolean;
389
+ completionBudget?: string;
390
+ diagnosticsDelay?: string;
391
+ experimentalPostfixCompletions?: boolean;
392
+ experimentalWorkspaceModule?: boolean;
393
+ experimentalTemplateSupport?: boolean;
394
+ semanticTokens?: boolean;
395
+ noSemanticString?: boolean;
396
+ noSemanticNumber?: boolean;
397
+ expandWorkspaceToModule?: boolean;
398
+ experimentalUseInvalidMetadata?: boolean;
399
+ hoverKind?: 'FullDocumentation' | 'NoDocumentation' | 'SingleLine' | 'Structured' | 'SynopsisDocumentation';
400
+ linkTarget?: string;
401
+ linksInHover?: boolean;
402
+ importShortcut?: 'Both' | 'Definition' | 'Link';
403
+ symbolMatcher?: 'CaseInsensitive' | 'CaseSensitive' | 'FastFuzzy' | 'Fuzzy';
404
+ symbolStyle?: 'Dynamic' | 'Full' | 'Package';
405
+ verboseOutput?: boolean;
406
+ }
407
+ export {};
package/dist/pos.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ import type { Text } from '@codemirror/state';
2
+ export declare function posToOffset(doc: Text, pos: {
3
+ line: number;
4
+ character: number;
5
+ }): number;
6
+ export declare function offsetToPos(doc: Text, offset: number): {
7
+ character: number;
8
+ line: number;
9
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codemirror-languageserver",
3
- "version": "1.14.0",
3
+ "version": "1.15.0",
4
4
  "description": "Language Server Plugin for CodeMirror 6",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",