codemirror-languageserver 1.14.0 → 1.16.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/dist/index.d.ts CHANGED
@@ -1,98 +1,15 @@
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;
1
+ export { LanguageServerClient, languageServerPlugin } from './plugin';
2
+ export { jumpToDefinition, jumpToDefinitionPos, jumpToDefinitionKeymap, } from './definition';
3
+ export { PyrightInitializationOptions, RustAnalyzerInitializationOptions, TypeScriptInitializationOptions, ESLintInitializationOptions, ClangdInitializationOptions, GoplsInitializationOptions, } from './initialization';
4
+ import { LanguageServerClient } from './plugin';
5
+ import type { LanguageServerClientOptions, LanguageServerBaseOptions } from './plugin';
6
+ interface LanguageServerOptions<InitializationOptions = unknown> extends LanguageServerClientOptions<InitializationOptions> {
7
+ client?: LanguageServerClient<InitializationOptions>;
87
8
  allowHTMLContent?: boolean;
88
9
  }
89
- interface LanguageServerWebsocketOptions extends LanguageServerBaseOptions {
10
+ interface LanguageServerWebsocketOptions<InitializationOptions = unknown> extends LanguageServerBaseOptions {
90
11
  serverUri: `ws://${string}` | `wss://${string}`;
12
+ initializationOptions?: InitializationOptions;
91
13
  }
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 {};
14
+ export declare function languageServer<InitializationOptions = unknown>(options: LanguageServerWebsocketOptions<InitializationOptions>): import("@codemirror/state").Extension[];
15
+ export declare function languageServerWithTransport<InitializationOptions = unknown>(options: LanguageServerOptions<InitializationOptions>): import("@codemirror/state").Extension[];
package/dist/index.js CHANGED
@@ -1,22 +1,41 @@
1
- import { autocompletion, insertCompletionText } from '@codemirror/autocomplete';
1
+ import { insertCompletionText, autocompletion as autocompletion$1 } from '@codemirror/autocomplete';
2
2
  import { setDiagnostics } from '@codemirror/lint';
3
3
  import { Facet } from '@codemirror/state';
4
- import { ViewPlugin, hoverTooltip } from '@codemirror/view';
4
+ import { ViewPlugin, hoverTooltip as hoverTooltip$1, EditorView, keymap } 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]));
12
30
  const useLast = (values) => values.reduce((_, v) => v, '');
13
- const client = Facet.define({
31
+ Facet.define({
14
32
  combine: useLast,
15
33
  });
16
- const documentUri = Facet.define({ combine: useLast });
17
- const languageId = Facet.define({ combine: useLast });
34
+ Facet.define({ combine: useLast });
35
+ 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,13 +63,14 @@ 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: {
52
72
  dynamicRegistration: true,
53
- contentFormat: ['plaintext', 'markdown'],
73
+ contentFormat: ['markdown', 'plaintext'],
54
74
  },
55
75
  moniker: {},
56
76
  synchronization: {
@@ -64,16 +84,16 @@ class LanguageServerClient {
64
84
  completionItem: {
65
85
  snippetSupport: false,
66
86
  commitCharactersSupport: true,
67
- documentationFormat: ['plaintext', 'markdown'],
87
+ documentationFormat: ['markdown', 'plaintext'],
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,
75
95
  signatureInformation: {
76
- documentationFormat: ['plaintext', 'markdown'],
96
+ documentationFormat: ['markdown', 'plaintext'],
77
97
  },
78
98
  },
79
99
  declaration: {
@@ -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,15 @@ 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
+ }
152
+ async textDocumentDeclaration(params) {
153
+ return await this.request('textDocument/declaration', params, timeout);
154
+ }
155
+ async textDocumentTypeDefinition(params) {
156
+ return await this.request('textDocument/typeDefinition', params, timeout);
157
+ }
126
158
  attachPlugin(plugin) {
127
159
  this.plugins.push(plugin);
128
160
  }
@@ -149,12 +181,12 @@ class LanguageServerClient {
149
181
  }
150
182
  }
151
183
  class LanguageServerPlugin {
152
- constructor(view, allowHTMLContent) {
184
+ constructor(view, { client, documentUri, languageId, allowHTMLContent, }) {
153
185
  this.view = view;
186
+ this.client = client;
187
+ this.documentUri = documentUri;
188
+ this.languageId = languageId;
154
189
  this.allowHTMLContent = allowHTMLContent;
155
- this.client = this.view.state.facet(client);
156
- this.documentUri = this.view.state.facet(documentUri);
157
- this.languageId = this.view.state.facet(languageId);
158
190
  this.documentVersion = 0;
159
191
  this.changesTimeout = 0;
160
192
  this.client.attachPlugin(this);
@@ -356,6 +388,44 @@ class LanguageServerPlugin {
356
388
  filter: false,
357
389
  };
358
390
  }
391
+ async requestLocation(view, { line, character }, capability, method) {
392
+ if (!this.client.ready || !this.client.capabilities[capability]) {
393
+ return null;
394
+ }
395
+ const result = await this.client[method]({
396
+ textDocument: { uri: this.documentUri },
397
+ position: { line, character },
398
+ });
399
+ if (!result)
400
+ return;
401
+ const location = Array.isArray(result) ? result[0] : result;
402
+ const uri = location.uri ||
403
+ location.targetUri;
404
+ const range = location.range ||
405
+ location.targetRange;
406
+ if (uri === this.documentUri) {
407
+ view.dispatch(view.state.update({
408
+ selection: {
409
+ anchor: posToOffset(view.state.doc, range.start),
410
+ head: posToOffset(view.state.doc, range.end),
411
+ },
412
+ scrollIntoView: true,
413
+ }));
414
+ }
415
+ return {
416
+ uri,
417
+ range,
418
+ };
419
+ }
420
+ async requestDefinition(view, { line, character }) {
421
+ return this.requestLocation(view, { line, character }, 'definitionProvider', 'textDocumentDefinition');
422
+ }
423
+ async requestDeclaration(view, { line, character }) {
424
+ return this.requestLocation(view, { line, character }, 'declarationProvider', 'textDocumentDeclaration');
425
+ }
426
+ async requestTypeDefinition(view, { line, character }) {
427
+ return this.requestLocation(view, { line, character }, 'typeDefinitionProvider', 'textDocumentTypeDefinition');
428
+ }
359
429
  processNotification(notification) {
360
430
  try {
361
431
  switch (notification.method) {
@@ -399,72 +469,7 @@ class LanguageServerPlugin {
399
469
  this.view.dispatch(setDiagnostics(this.view.state, diagnostics));
400
470
  }
401
471
  }
402
- function languageServer(options) {
403
- const serverUri = options.serverUri;
404
- delete options.serverUri;
405
- return languageServerWithTransport({
406
- ...options,
407
- transport: new WebSocketTransport(serverUri),
408
- });
409
- }
410
- function languageServerWithTransport(options) {
411
- let plugin = null;
412
- return [
413
- client.of(options.client ||
414
- new LanguageServerClient({ ...options, autoClose: true })),
415
- documentUri.of(options.documentUri),
416
- languageId.of(options.languageId),
417
- ViewPlugin.define((view) => (plugin = new LanguageServerPlugin(view, options.allowHTMLContent))),
418
- hoverTooltip((view, pos) => {
419
- 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;
421
- }),
422
- autocompletion({
423
- override: [
424
- async (context) => {
425
- var _a, _b, _c;
426
- if (plugin == null) {
427
- return null;
428
- }
429
- const { state, pos, explicit } = context;
430
- const line = state.doc.lineAt(pos);
431
- let trigKind = CompletionTriggerKind.Invoked;
432
- let trigChar;
433
- if (!explicit &&
434
- ((_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]))) {
435
- trigKind = CompletionTriggerKind.TriggerCharacter;
436
- trigChar = line.text[pos - line.from - 1];
437
- }
438
- if (trigKind === CompletionTriggerKind.Invoked &&
439
- !context.matchBefore(/\w+$/)) {
440
- return null;
441
- }
442
- return await plugin.requestCompletion(context, offsetToPos(state.doc, pos), {
443
- triggerCharacter: trigChar,
444
- triggerKind: trigKind,
445
- });
446
- },
447
- ],
448
- }),
449
- ];
450
- }
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
- }
472
+ const languageServerPlugin = ViewPlugin.fromClass(LanguageServerPlugin);
468
473
  async function formatContents(contents) {
469
474
  if (isLSPMarkupContent(contents)) {
470
475
  let value = contents.value;
@@ -495,10 +500,11 @@ function prefixMatch(items) {
495
500
  const first = new Set();
496
501
  const rest = new Set();
497
502
  for (const item of items) {
498
- const [initial, ...restStr] = ((_a = item.textEdit) === null || _a === void 0 ? void 0 : _a.newText) || item.label;
503
+ const text = ((_a = item.textEdit) === null || _a === void 0 ? void 0 : _a.newText) || item.label;
504
+ const initial = text[0];
499
505
  first.add(initial);
500
- for (const char of restStr) {
501
- rest.add(char);
506
+ for (let i = 1; i < text.length; i++) {
507
+ rest.add(text[i]);
502
508
  }
503
509
  }
504
510
  const source = toSet(first) + toSet(rest) + '*$';
@@ -511,4 +517,104 @@ function isLSPMarkupContent(contents) {
511
517
  return contents.kind !== undefined;
512
518
  }
513
519
 
514
- export { LanguageServerClient, languageServer, languageServerWithTransport };
520
+ function requestDefinition(view, pos) {
521
+ const plugin = view.plugin(languageServerPlugin);
522
+ if (!plugin)
523
+ return false;
524
+ plugin.requestDefinition(view, offsetToPos(view.state.doc, pos));
525
+ return true;
526
+ }
527
+ function requestTypeDefinition(view, pos) {
528
+ const plugin = view.plugin(languageServerPlugin);
529
+ if (!plugin)
530
+ return false;
531
+ plugin.requestTypeDefinition(view, offsetToPos(view.state.doc, pos));
532
+ return true;
533
+ }
534
+ const jumpToDefinition = (view) => requestDefinition(view, view.state.selection.main.head);
535
+ const jumpToDefinitionPos = (pos) => (view) => requestDefinition(view, pos);
536
+ const jumpToTypeDefinition = (view) => requestTypeDefinition(view, view.state.selection.main.head);
537
+ const jumpToTypeDefinitionPos = (pos) => (view) => requestTypeDefinition(view, pos);
538
+ const jumpToDefinitionKeymap = [
539
+ { key: 'F12', run: jumpToDefinition, preventDefault: true },
540
+ { key: 'Mod-F12', run: jumpToTypeDefinition, preventDefault: true },
541
+ ];
542
+
543
+ const hoverTooltip = () => hoverTooltip$1((view, pos) => {
544
+ var _a;
545
+ const plugin = view.plugin(languageServerPlugin);
546
+ return ((_a = plugin === null || plugin === void 0 ? void 0 : plugin.requestHoverTooltip(view, offsetToPos(view.state.doc, pos))) !== null && _a !== void 0 ? _a : null);
547
+ });
548
+
549
+ const autocompletion = () => autocompletion$1({
550
+ override: [
551
+ async (context) => {
552
+ var _a, _b, _c;
553
+ const { state, pos, explicit, view } = context;
554
+ const plugin = view.plugin(languageServerPlugin);
555
+ if (plugin == null)
556
+ return null;
557
+ const line = state.doc.lineAt(pos);
558
+ let trigKind = CompletionTriggerKind.Invoked;
559
+ let trigChar;
560
+ if (!explicit &&
561
+ ((_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]))) {
562
+ trigKind = CompletionTriggerKind.TriggerCharacter;
563
+ trigChar = line.text[pos - line.from - 1];
564
+ }
565
+ if (trigKind === CompletionTriggerKind.Invoked &&
566
+ !context.matchBefore(/\w+$/)) {
567
+ return null;
568
+ }
569
+ return await plugin.requestCompletion(context, offsetToPos(state.doc, pos), {
570
+ triggerCharacter: trigChar,
571
+ triggerKind: trigKind,
572
+ });
573
+ },
574
+ ],
575
+ });
576
+
577
+ const mouseHandler = () => EditorView.domEventHandlers({
578
+ mousedown: (event, view) => {
579
+ if (!event.ctrlKey && !event.metaKey)
580
+ return;
581
+ const pos = view.posAtCoords({
582
+ x: event.clientX,
583
+ y: event.clientY,
584
+ });
585
+ const ok = (event.shiftKey
586
+ ? jumpToTypeDefinitionPos(pos)
587
+ : jumpToDefinitionPos(pos))(view);
588
+ if (ok)
589
+ event.preventDefault();
590
+ },
591
+ });
592
+
593
+ function languageServer(options) {
594
+ const serverUri = options.serverUri;
595
+ const { serverUri: _, ...optionsWithoutServerUri } = options;
596
+ return languageServerWithTransport({
597
+ ...optionsWithoutServerUri,
598
+ transport: new WebSocketTransport(serverUri),
599
+ });
600
+ }
601
+ function languageServerWithTransport(options) {
602
+ return [
603
+ languageServerPlugin.of({
604
+ client: options.client ||
605
+ new LanguageServerClient({
606
+ ...options,
607
+ autoClose: true,
608
+ }),
609
+ documentUri: options.documentUri,
610
+ languageId: options.languageId,
611
+ allowHTMLContent: options.allowHTMLContent,
612
+ }),
613
+ hoverTooltip(),
614
+ autocompletion(),
615
+ keymap.of([...jumpToDefinitionKeymap]),
616
+ mouseHandler(),
617
+ ];
618
+ }
619
+
620
+ export { LanguageServerClient, jumpToDefinition, jumpToDefinitionKeymap, jumpToDefinitionPos, languageServer, languageServerPlugin, languageServerWithTransport };