@adecore/editor-react 0.41.1 → 0.43.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.
Files changed (124) hide show
  1. package/dist/CallHierarchyPanel.js +177 -181
  2. package/dist/EditorContextMenu.js +118 -105
  3. package/dist/EditorView.js +7 -1
  4. package/dist/LanguagePopups.js +7 -0
  5. package/dist/PickPopup.js +190 -70
  6. package/dist/WrapCard.d.ts +8 -0
  7. package/dist/WrapCard.js +172 -0
  8. package/dist/call-hierarchy-model.d.ts +4 -2
  9. package/dist/call-hierarchy-model.js +20 -0
  10. package/dist/call-hierarchy.d.ts +3 -0
  11. package/dist/call-hierarchy.js +54 -22
  12. package/dist/clipboard-history.d.ts +31 -0
  13. package/dist/clipboard-history.js +70 -0
  14. package/dist/code-actions.d.ts +3 -1
  15. package/dist/code-actions.js +50 -10
  16. package/dist/code-vision.js +2 -3
  17. package/dist/completion.js +10 -15
  18. package/dist/context-menu-rows.d.ts +1 -1
  19. package/dist/context-menu-rows.js +4 -0
  20. package/dist/definition-link.js +15 -5
  21. package/dist/document-colors.d.ts +24 -0
  22. package/dist/document-colors.js +194 -0
  23. package/dist/document-links.d.ts +22 -0
  24. package/dist/document-links.js +133 -0
  25. package/dist/document-query.d.ts +15 -0
  26. package/dist/document-query.js +54 -0
  27. package/dist/edit-locations.d.ts +20 -0
  28. package/dist/edit-locations.js +136 -0
  29. package/dist/editor-language.d.ts +19 -1
  30. package/dist/editor-language.js +77 -2
  31. package/dist/emmet.d.ts +13 -0
  32. package/dist/emmet.js +62 -0
  33. package/dist/folding.js +2 -3
  34. package/dist/format-on-type.js +3 -5
  35. package/dist/highlights.js +2 -3
  36. package/dist/history.d.ts +7 -0
  37. package/dist/history.js +79 -1
  38. package/dist/host-types.d.ts +6 -0
  39. package/dist/index.d.ts +11 -2
  40. package/dist/index.js +8 -0
  41. package/dist/inlay-hints.js +2 -2
  42. package/dist/inline-completion.d.ts +39 -0
  43. package/dist/inline-completion.js +221 -0
  44. package/dist/key-priority.d.ts +8 -5
  45. package/dist/key-priority.js +9 -6
  46. package/dist/linked-editing.d.ts +26 -0
  47. package/dist/linked-editing.js +215 -0
  48. package/dist/locales/en.json +31 -2
  49. package/dist/locales/nl.json +31 -2
  50. package/dist/markup-context.d.ts +37 -0
  51. package/dist/markup-context.js +291 -0
  52. package/dist/markup.d.ts +36 -0
  53. package/dist/markup.js +453 -0
  54. package/dist/navigation.js +4 -6
  55. package/dist/paste-history.d.ts +7 -0
  56. package/dist/paste-history.js +104 -0
  57. package/dist/peek.js +3 -4
  58. package/dist/pick.d.ts +20 -2
  59. package/dist/pick.js +141 -18
  60. package/dist/popups.d.ts +9 -0
  61. package/dist/popups.js +2 -1
  62. package/dist/project-language.d.ts +18 -1
  63. package/dist/project-language.js +129 -18
  64. package/dist/rename.js +8 -11
  65. package/dist/rendered-docs.js +1 -2
  66. package/dist/selection-ranges.js +2 -3
  67. package/dist/shortcut-keys.d.ts +27 -0
  68. package/dist/signature.js +3 -6
  69. package/dist/snippet-session.d.ts +2 -2
  70. package/dist/snippet-session.js +3 -3
  71. package/dist/symbol-picker.js +4 -6
  72. package/dist/symbols.js +22 -3
  73. package/dist/testing.d.ts +23 -1
  74. package/dist/testing.js +114 -0
  75. package/package.json +7 -6
  76. package/src/CallHierarchyPanel.tsx +26 -11
  77. package/src/EditorContextMenu.tsx +7 -0
  78. package/src/EditorView.tsx +9 -0
  79. package/src/LanguagePopups.tsx +4 -0
  80. package/src/PickPopup.tsx +40 -5
  81. package/src/WrapCard.tsx +64 -0
  82. package/src/call-hierarchy-model.ts +25 -3
  83. package/src/call-hierarchy.ts +62 -20
  84. package/src/clipboard-history.ts +102 -0
  85. package/src/code-actions.ts +48 -11
  86. package/src/code-vision.ts +3 -3
  87. package/src/completion.ts +15 -15
  88. package/src/context-menu-rows.ts +3 -2
  89. package/src/definition-link.ts +17 -5
  90. package/src/document-colors.ts +189 -0
  91. package/src/document-links.ts +129 -0
  92. package/src/document-query.ts +71 -0
  93. package/src/edit-locations.ts +162 -0
  94. package/src/editor-language.ts +80 -3
  95. package/src/emmet-module.d.ts +44 -0
  96. package/src/emmet.ts +73 -0
  97. package/src/folding.ts +3 -3
  98. package/src/format-on-type.ts +5 -5
  99. package/src/highlights.ts +3 -3
  100. package/src/history.ts +81 -1
  101. package/src/host-types.ts +10 -0
  102. package/src/index.ts +12 -3
  103. package/src/inlay-hints.ts +2 -2
  104. package/src/inline-completion.ts +221 -0
  105. package/src/key-priority.ts +9 -6
  106. package/src/linked-editing.ts +199 -0
  107. package/src/locales/en.json +31 -2
  108. package/src/locales/nl.json +31 -2
  109. package/src/markup-context.ts +262 -0
  110. package/src/markup.ts +367 -0
  111. package/src/navigation.ts +6 -6
  112. package/src/paste-history.ts +111 -0
  113. package/src/peek.ts +4 -4
  114. package/src/pick.ts +201 -19
  115. package/src/popups.ts +19 -1
  116. package/src/project-language.ts +136 -19
  117. package/src/rename.ts +11 -11
  118. package/src/rendered-docs.ts +2 -2
  119. package/src/selection-ranges.ts +3 -3
  120. package/src/signature.ts +6 -6
  121. package/src/snippet-session.ts +9 -4
  122. package/src/symbol-picker.ts +6 -6
  123. package/src/symbols.ts +30 -4
  124. package/src/testing.ts +126 -1
package/src/rename.ts CHANGED
@@ -67,9 +67,9 @@ export class RenameFeature {
67
67
  }
68
68
 
69
69
  async start(): Promise<void> {
70
- const { editor, project, uri } = this.language;
70
+ const { editor, uri } = this.language;
71
71
 
72
- if (!project.service.supports(METHOD, uri)) {
72
+ if (!this.language.service.supports(METHOD, uri)) {
73
73
  this.tell('error', this.say('unavailable'));
74
74
 
75
75
  return;
@@ -77,11 +77,11 @@ export class RenameFeature {
77
77
 
78
78
  this.cancel();
79
79
  const caret = editor.getCaret();
80
- const prepare = project.service.providerOptions(METHOD, uri)?.prepareProvider === true;
80
+ const prepare = this.language.service.providerOptions(METHOD, uri)?.prepareProvider === true;
81
81
  let answer: PrepareRenameResult;
82
82
 
83
83
  try {
84
- answer = prepare ? await project.service.prepareRename(uri, caret) : { defaultBehavior: true };
84
+ answer = prepare ? await this.language.service.prepareRename(uri, caret) : { defaultBehavior: true };
85
85
  } catch (error) {
86
86
  if (!(error instanceof StaleResultError)) {
87
87
  this.tell('error', this.say('nothing'));
@@ -127,7 +127,7 @@ export class RenameFeature {
127
127
  const { project, uri } = this.language;
128
128
 
129
129
  try {
130
- const edit = await project.service.rename(uri, session.target.range.start, next);
130
+ const edit = await this.language.service.rename(uri, session.target.range.start, next);
131
131
 
132
132
  if (this.session !== session) {
133
133
  return;
@@ -218,14 +218,14 @@ export class RenameFeature {
218
218
 
219
219
  /* Lights up the places the name has in this file, and counts them all; what the servers listed, or null when they did not. */
220
220
  private async light(session: Session): Promise<readonly Location[] | null> {
221
- const { editor, project, uri } = this.language;
221
+ const { editor, uri } = this.language;
222
222
 
223
- if (!project.service.supports('textDocument/references', uri)) {
223
+ if (!this.language.service.supports('textDocument/references', uri)) {
224
224
  return null;
225
225
  }
226
226
 
227
227
  try {
228
- const locations = await project.service.references(uri, session.target.range.start, true);
228
+ const locations = await this.language.service.references(uri, session.target.range.start, true);
229
229
 
230
230
  if (this.session !== session || locations === null) {
231
231
  return null;
@@ -277,14 +277,14 @@ export class RenameFeature {
277
277
 
278
278
  /* So a suggester knows a function from a variable; a rename on a use finds the kind through the declaration's place in this file. */
279
279
  private async kindOf(session: Session, locations: readonly Location[]): Promise<string | undefined> {
280
- const { project, uri } = this.language;
280
+ const { uri } = this.language;
281
281
 
282
- if (!project.service.supports('textDocument/documentSymbol', uri)) {
282
+ if (!this.language.service.supports('textDocument/documentSymbol', uri)) {
283
283
  return undefined;
284
284
  }
285
285
 
286
286
  try {
287
- const symbols = await project.service.documentSymbols(uri, { signal: session.names.signal, parallel: true });
287
+ const symbols = await this.language.service.documentSymbols(uri, { signal: session.names.signal, parallel: true });
288
288
 
289
289
  return renameKindOf(symbols, session.original, [session.target.range, ...occurrencesOf(locations, uri).inFile]);
290
290
  } catch {
@@ -254,9 +254,9 @@ export class RenderedDocsFeature {
254
254
 
255
255
  /* Whether names in `{@link}` and `@see` can be followed, which takes a server that finds symbols by name. */
256
256
  get canFollow(): boolean {
257
- const { project, uri } = this.language;
257
+ const { uri } = this.language;
258
258
 
259
- return project.service.supports('workspace/symbol', uri);
259
+ return this.language.service.supports('workspace/symbol', uri);
260
260
  }
261
261
 
262
262
  /* Goes to what a link in a comment names. */
@@ -21,14 +21,14 @@ export function selectionChainOf(selection: SelectionRange): EditorRange[] {
21
21
  */
22
22
  export class SelectionRangesFeature {
23
23
  constructor(language: EditorLanguage) {
24
- const { editor, project, uri } = language;
24
+ const { editor, uri } = language;
25
25
 
26
26
  editor.setSelectionRanges(async (positions) => {
27
- if (!project.service.supports(METHOD, uri)) {
27
+ if (!language.service.supports(METHOD, uri)) {
28
28
  return null;
29
29
  }
30
30
 
31
- const chains = await project.service.selectionRanges(uri, positions);
31
+ const chains = await language.service.selectionRanges(uri, positions);
32
32
 
33
33
  return chains === null || chains.length !== positions.length ? null : chains.map(selectionChainOf);
34
34
  });
package/src/signature.ts CHANGED
@@ -111,8 +111,8 @@ export class SignatureFeature {
111
111
  }
112
112
 
113
113
  private options(): { triggers: readonly string[]; retriggers: readonly string[] } | null {
114
- const { project, uri } = this.language;
115
- const options = project.service.providerOptions(METHOD, uri);
114
+ const { uri } = this.language;
115
+ const options = this.language.service.providerOptions(METHOD, uri);
116
116
 
117
117
  return options === undefined ? null : { triggers: options.triggerCharacters ?? [], retriggers: options.retriggerCharacters ?? [] };
118
118
  }
@@ -147,9 +147,9 @@ export class SignatureFeature {
147
147
  }
148
148
 
149
149
  private ask(context: Omit<SignatureHelpContext, 'activeSignatureHelp'>): void {
150
- const { project, uri } = this.language;
150
+ const { uri } = this.language;
151
151
 
152
- if (!project.service.supports(METHOD, uri)) {
152
+ if (!this.language.service.supports(METHOD, uri)) {
153
153
  return;
154
154
  }
155
155
 
@@ -160,7 +160,7 @@ export class SignatureFeature {
160
160
  }
161
161
 
162
162
  private async fetch(context: SignatureHelpContext, request: number): Promise<void> {
163
- const { editor, project, uri } = this.language;
163
+ const { editor, uri } = this.language;
164
164
  const caret = editor.getCaret();
165
165
  this.controller?.abort();
166
166
  const controller = new AbortController();
@@ -168,7 +168,7 @@ export class SignatureFeature {
168
168
  let help: SignatureHelp | null;
169
169
 
170
170
  try {
171
- help = await project.service.signatureHelp(uri, caret, context, { signal: controller.signal });
171
+ help = await this.language.service.signatureHelp(uri, caret, context, { signal: controller.signal });
172
172
  } catch {
173
173
  return;
174
174
  }
@@ -5,7 +5,7 @@ import { acquireSnippetTransforms, type SnippetTransformLease } from './snippet-
5
5
  import { realTimers, type Timers } from './timers.ts';
6
6
  import { KEY_PRIORITY } from './key-priority.ts';
7
7
  import { MAX_TRANSFORM_CHARACTERS, type SnippetTransformJob } from './snippet-transform-protocol.ts';
8
- import type { EditorContentChange, EditorPosition, EditorRange, EditorTextChange } from '@adecore/editor';
8
+ import type { EditorApplyEditsOptions, EditorContentChange, EditorPosition, EditorRange, EditorTextChange } from '@adecore/editor';
9
9
  import { comparePositions, endOfInsertion, rangeHolds, sameRange, shiftPosition } from './diagnostics-model.ts';
10
10
  import type { EditorLanguage } from './editor-language.ts';
11
11
  import { textBeforeOf } from './line-text.ts';
@@ -367,7 +367,12 @@ export class SnippetFeature {
367
367
  }
368
368
 
369
369
  /* Imports can touch a hull's edge; their affinity must differ from edits inside its active field. */
370
- applyEdits(changes: readonly EditorContentChange[], token: number, extras: readonly EditorContentChange[] = []): boolean {
370
+ applyEdits(
371
+ changes: readonly EditorContentChange[],
372
+ token: number,
373
+ extras: readonly EditorContentChange[] = [],
374
+ options?: EditorApplyEditsOptions
375
+ ): boolean {
371
376
  const frame = this.frames.at(-1);
372
377
 
373
378
  const fits =
@@ -385,14 +390,14 @@ export class SnippetFeature {
385
390
  if (token !== this.generation || !fits) {
386
391
  this.end();
387
392
 
388
- return this.language.editor.applyEdits(changes);
393
+ return this.language.editor.applyEdits(changes, options);
389
394
  }
390
395
 
391
396
  this.applying = true;
392
397
  this.extras = extras;
393
398
 
394
399
  try {
395
- return this.language.editor.applyEdits(changes);
400
+ return this.language.editor.applyEdits(changes, options);
396
401
  } finally {
397
402
  this.applying = false;
398
403
  this.extras = [];
@@ -36,16 +36,16 @@ export class SymbolPickerFeature {
36
36
  }
37
37
 
38
38
  async open(): Promise<void> {
39
- const { project, uri, popups } = this.language;
39
+ const { uri, popups } = this.language;
40
40
 
41
- if (!project.service.supports('textDocument/documentSymbol', uri)) {
41
+ if (!this.language.service.supports('textDocument/documentSymbol', uri)) {
42
42
  this.language.project.host.notify?.({ id: TOAST_ID, kind: 'error', title: this.language.project.i18n.t('editor:language.symbols.unavailable') });
43
43
 
44
44
  return;
45
45
  }
46
46
 
47
47
  try {
48
- const entries = entriesOf(await project.service.documentSymbols(uri));
48
+ const entries = entriesOf(await this.language.service.documentSymbols(uri));
49
49
  popups.setState({ symbols: { file: basenameOf(this.language.uri), entries } });
50
50
  } catch (error) {
51
51
  if (!(error instanceof StaleResultError)) {
@@ -79,13 +79,13 @@ export class SymbolPickerFeature {
79
79
 
80
80
  /* The symbols of the project that answer to a name, which is a request to the server and so waits for a pause in the typing. */
81
81
  async search(text: string, signal: AbortSignal): Promise<WorkspaceEntry[]> {
82
- const { project, uri } = this.language;
82
+ const { uri } = this.language;
83
83
 
84
- if (text === '' || !project.service.supports('workspace/symbol', uri)) {
84
+ if (text === '' || !this.language.service.supports('workspace/symbol', uri)) {
85
85
  return [];
86
86
  }
87
87
 
88
- return workspaceEntriesOf(await project.service.workspaceSymbols(uri, text, { signal }));
88
+ return workspaceEntriesOf(await this.language.service.workspaceSymbols(uri, text, { signal }));
89
89
  }
90
90
 
91
91
  goToWorkspace(entry: WorkspaceEntry): void {
package/src/symbols.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { EditorBlock } from '@adecore/editor';
1
+ import type { EditorBlock, EditorPosition } from '@adecore/editor';
2
2
  import type { DocumentSymbol, DocumentSymbolResult, Range, SymbolInformation } from '@adecore/lsp';
3
3
  import type { EditorLanguage } from './editor-language.ts';
4
4
  import { Refresher } from './refresher.ts';
@@ -55,6 +55,29 @@ export function symbolSpansOf(result: DocumentSymbolResult): SymbolSpan[] {
55
55
  return (result as SymbolInformation[]).map((symbol) => ({ name: symbol.name, kind: symbol.kind, range: symbol.location.range }));
56
56
  }
57
57
 
58
+ function declarationsOf(result: DocumentSymbolResult): EditorPosition[] {
59
+ if (result === null || result.length === 0) {
60
+ return [];
61
+ }
62
+
63
+ if (!('range' in result[0]!)) {
64
+ return (result as SymbolInformation[]).map((symbol) => symbol.location.range.start);
65
+ }
66
+
67
+ const stops: EditorPosition[] = [];
68
+
69
+ const visit = (symbols: readonly DocumentSymbol[]): void => {
70
+ for (const symbol of symbols) {
71
+ stops.push((symbol.selectionRange ?? symbol.range).start);
72
+ visit(symbol.children ?? []);
73
+ }
74
+ };
75
+
76
+ visit(result as DocumentSymbol[]);
77
+
78
+ return stops;
79
+ }
80
+
58
81
  /* A line a declaration can have above it: blank, a comment, or an attribute. A bare `#` is no comment, since it heads a Markdown section. */
59
82
  const PRELUDE = /^\s*(?:$|\/\/|\/\*|\*|#\[)/;
60
83
 
@@ -121,18 +144,20 @@ export class SymbolsFeature {
121
144
  private readonly listeners = new Set<(result: DocumentSymbolResult) => void>();
122
145
 
123
146
  constructor(language: EditorLanguage, timers: Timers = realTimers) {
124
- const { editor, project, uri } = language;
147
+ const { editor, uri } = language;
125
148
 
126
149
  this.refresher = new Refresher(
127
150
  async (signal) => {
128
- if (!project.service.supports(METHOD, uri)) {
151
+ if (!language.service.supports(METHOD, uri)) {
129
152
  return;
130
153
  }
131
154
 
132
- const result = await project.service.documentSymbols(uri, { signal });
155
+ const result = await language.service.documentSymbols(uri, { signal });
133
156
 
134
157
  if (!signal.aborted) {
135
158
  editor.setBlocks(result === null ? null : blocksOf(result, editor.getText()));
159
+ const declarations = declarationsOf(result);
160
+ editor.setDeclarations?.(declarations.length === 0 ? null : declarations);
136
161
 
137
162
  for (const listener of [...this.listeners]) {
138
163
  listener(result);
@@ -154,6 +179,7 @@ export class SymbolsFeature {
154
179
  providers();
155
180
  this.refresher.dispose();
156
181
  editor.setBlocks(null);
182
+ editor.setDeclarations?.(null);
157
183
  });
158
184
  }
159
185
 
package/src/testing.ts CHANGED
@@ -38,7 +38,24 @@ import {
38
38
  type SignatureHelpContext,
39
39
  type TextEdit,
40
40
  type WorkspaceEdit,
41
- type WorkspaceSymbolResult
41
+ type WorkspaceSymbolResult,
42
+ type Color,
43
+ type ColorInformation,
44
+ type ColorPresentation,
45
+ type DocumentLink,
46
+ type InlineCompletionContext,
47
+ type InlineCompletionResult,
48
+ type InlineValue,
49
+ type InlineValueContext,
50
+ type LinkedEditingRanges,
51
+ type Moniker,
52
+ type PreviousResultId,
53
+ type ServerMessage,
54
+ type ShowDocumentParams,
55
+ type ShowDocumentResult,
56
+ type TextDocumentSaveReason,
57
+ type TypeHierarchyItem,
58
+ type WorkspaceDiagnosticReport
42
59
  } from '@adecore/lsp';
43
60
 
44
61
  export { ManualTimers } from './timers.ts';
@@ -57,6 +74,8 @@ export class FakeLanguageService implements LanguageService {
57
74
  private readonly answers = new Map<string, (call: LanguageCall) => unknown>();
58
75
  private readonly diagnostics = new Set<(report: DiagnosticsReport) => void>();
59
76
  private readonly changes = new Set<(uri: string) => void>();
77
+ private readonly messages = new Set<(message: ServerMessage) => void>();
78
+ private showDocument: ((params: ShowDocumentParams) => Promise<ShowDocumentResult>) | null = null;
60
79
 
61
80
  respond(method: string, answer: (call: LanguageCall) => unknown, provider: ProviderOptions = {}): void {
62
81
  this.answers.set(method, answer);
@@ -271,4 +290,110 @@ export class FakeLanguageService implements LanguageService {
271
290
  resolveCodeLens(uri: string, lens: CodeLens, options: LanguageRequestOptions = {}): Promise<CodeLens> {
272
291
  return this.request<CodeLens>('codeLens/resolve', uri, lens, options);
273
292
  }
293
+
294
+ documentLinks(uri: string, options: LanguageRequestOptions = {}): Promise<DocumentLink[] | null> {
295
+ return this.request<DocumentLink[] | null>('textDocument/documentLink', uri, {}, options);
296
+ }
297
+
298
+ resolveDocumentLink(uri: string, link: DocumentLink, options: LanguageRequestOptions = {}): Promise<DocumentLink> {
299
+ return this.request<DocumentLink>('documentLink/resolve', uri, link, options);
300
+ }
301
+
302
+ documentColors(uri: string, options: LanguageRequestOptions = {}): Promise<ColorInformation[] | null> {
303
+ return this.request<ColorInformation[] | null>('textDocument/documentColor', uri, {}, options);
304
+ }
305
+
306
+ colorPresentations(uri: string, color: Color, range: Range, options: LanguageRequestOptions = {}): Promise<ColorPresentation[] | null> {
307
+ return this.request<ColorPresentation[] | null>('textDocument/colorPresentation', uri, { color, range }, options);
308
+ }
309
+
310
+ linkedEditingRanges(uri: string, position: Position, options: LanguageRequestOptions = {}): Promise<LinkedEditingRanges | null> {
311
+ return this.request<LinkedEditingRanges | null>('textDocument/linkedEditingRange', uri, { position }, options);
312
+ }
313
+
314
+ monikers(uri: string, position: Position, options: LanguageRequestOptions = {}): Promise<Moniker[] | null> {
315
+ return this.request<Moniker[] | null>('textDocument/moniker', uri, { position }, options);
316
+ }
317
+
318
+ prepareTypeHierarchy(uri: string, position: Position, options: LanguageRequestOptions = {}): Promise<TypeHierarchyItem[] | null> {
319
+ return this.request<TypeHierarchyItem[] | null>('textDocument/prepareTypeHierarchy', uri, { position }, options);
320
+ }
321
+
322
+ supertypes(uri: string, item: TypeHierarchyItem, options: LanguageRequestOptions = {}): Promise<TypeHierarchyItem[] | null> {
323
+ return this.request<TypeHierarchyItem[] | null>('typeHierarchy/supertypes', uri, { item }, options);
324
+ }
325
+
326
+ subtypes(uri: string, item: TypeHierarchyItem, options: LanguageRequestOptions = {}): Promise<TypeHierarchyItem[] | null> {
327
+ return this.request<TypeHierarchyItem[] | null>('typeHierarchy/subtypes', uri, { item }, options);
328
+ }
329
+
330
+ inlineValues(uri: string, range: Range, context: InlineValueContext, options: LanguageRequestOptions = {}): Promise<InlineValue[] | null> {
331
+ return this.request<InlineValue[] | null>('textDocument/inlineValue', uri, { range, context }, options);
332
+ }
333
+
334
+ inlineCompletions(
335
+ uri: string,
336
+ position: Position,
337
+ context: InlineCompletionContext,
338
+ options: LanguageRequestOptions = {}
339
+ ): Promise<InlineCompletionResult> {
340
+ return this.request<InlineCompletionResult>('textDocument/inlineCompletion', uri, { position, context }, options);
341
+ }
342
+
343
+ rangesFormatting(uri: string, ranges: readonly Range[], formatting: FormattingOptions, options: LanguageRequestOptions = {}): Promise<TextEdit[] | null> {
344
+ return this.request<TextEdit[] | null>('textDocument/rangesFormatting', uri, { ranges, options: formatting }, options);
345
+ }
346
+
347
+ async willSave(uri: string, reason: TextDocumentSaveReason = 1): Promise<void> {
348
+ this.calls.push({ method: 'textDocument/willSave', uri, params: { reason }, options: {} });
349
+ }
350
+
351
+ async willSaveWaitUntil(uri: string, reason: TextDocumentSaveReason = 1, options: LanguageRequestOptions = {}): Promise<TextEdit[] | null> {
352
+ return this.supports('textDocument/willSaveWaitUntil', uri)
353
+ ? this.request<TextEdit[] | null>('textDocument/willSaveWaitUntil', uri, { reason }, options)
354
+ : null;
355
+ }
356
+
357
+ workspaceDiagnostics(
358
+ uri: string,
359
+ previousResultIds: readonly PreviousResultId[] = [],
360
+ options: LanguageRequestOptions = {}
361
+ ): Promise<WorkspaceDiagnosticReport> {
362
+ return this.request<WorkspaceDiagnosticReport>('workspace/diagnostic', uri, { previousResultIds }, options);
363
+ }
364
+
365
+ async textDocumentContent(uri: string, options: LanguageRequestOptions = {}): Promise<string | null> {
366
+ if (!this.supports('workspace/textDocumentContent', uri)) {
367
+ return null;
368
+ }
369
+ return (await this.request<{ text: string } | null>('workspace/textDocumentContent', uri, { uri }, options))?.text ?? null;
370
+ }
371
+
372
+ onServerMessage(listener: (message: ServerMessage) => void): Disposable {
373
+ this.messages.add(listener);
374
+ return { dispose: () => this.messages.delete(listener) };
375
+ }
376
+
377
+ onShowDocument(handler: (params: ShowDocumentParams) => Promise<ShowDocumentResult>): Disposable {
378
+ this.showDocument = handler;
379
+ return {
380
+ dispose: () => {
381
+ if (this.showDocument === handler) {
382
+ this.showDocument = null;
383
+ }
384
+ }
385
+ };
386
+ }
387
+
388
+ /* Sends a message as a server would. */
389
+ message(message: ServerMessage): void {
390
+ for (const listener of this.messages) {
391
+ listener(message);
392
+ }
393
+ }
394
+
395
+ /* Asks the host to show a document as a server would; false when nothing handles it. */
396
+ async show(params: ShowDocumentParams): Promise<boolean> {
397
+ return (await this.showDocument?.(params))?.success ?? false;
398
+ }
274
399
  }