@adecore/editor-react 0.42.0 → 0.44.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 (150) hide show
  1. package/dist/CallHierarchyPanel.js +177 -181
  2. package/dist/EditorContextMenu.js +118 -105
  3. package/dist/LanguagePopups.js +7 -0
  4. package/dist/PickPopup.js +245 -104
  5. package/dist/WrapCard.d.ts +8 -0
  6. package/dist/WrapCard.js +172 -0
  7. package/dist/call-hierarchy-model.d.ts +4 -2
  8. package/dist/call-hierarchy-model.js +20 -0
  9. package/dist/call-hierarchy.d.ts +3 -0
  10. package/dist/call-hierarchy.js +54 -22
  11. package/dist/clipboard-history.d.ts +31 -0
  12. package/dist/clipboard-history.js +70 -0
  13. package/dist/code-actions.d.ts +3 -1
  14. package/dist/code-actions.js +50 -10
  15. package/dist/code-vision.js +2 -3
  16. package/dist/completion.d.ts +28 -0
  17. package/dist/completion.js +83 -27
  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 +23 -1
  30. package/dist/editor-language.js +83 -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 +10 -0
  39. package/dist/index.d.ts +13 -2
  40. package/dist/index.js +10 -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 +9 -5
  45. package/dist/key-priority.js +11 -6
  46. package/dist/linked-editing.d.ts +26 -0
  47. package/dist/linked-editing.js +215 -0
  48. package/dist/live-templates.d.ts +16 -0
  49. package/dist/live-templates.js +289 -0
  50. package/dist/locales/en.json +61 -2
  51. package/dist/locales/nl.json +61 -2
  52. package/dist/markup-context.d.ts +55 -0
  53. package/dist/markup-context.js +408 -0
  54. package/dist/markup.d.ts +39 -0
  55. package/dist/markup.js +465 -0
  56. package/dist/navigation.js +4 -6
  57. package/dist/paste-history.d.ts +7 -0
  58. package/dist/paste-history.js +104 -0
  59. package/dist/peek.js +3 -4
  60. package/dist/pick.d.ts +21 -2
  61. package/dist/pick.js +152 -19
  62. package/dist/popups.d.ts +10 -0
  63. package/dist/popups.js +2 -1
  64. package/dist/postfix.d.ts +21 -0
  65. package/dist/postfix.js +212 -0
  66. package/dist/project-language.d.ts +20 -1
  67. package/dist/project-language.js +132 -18
  68. package/dist/rename.js +8 -11
  69. package/dist/rendered-docs.js +1 -2
  70. package/dist/selection-ranges.js +2 -3
  71. package/dist/shortcut-keys.d.ts +24 -0
  72. package/dist/signature.js +3 -6
  73. package/dist/snippet-insert.d.ts +10 -0
  74. package/dist/snippet-insert.js +217 -0
  75. package/dist/snippet-session.d.ts +2 -2
  76. package/dist/snippet-session.js +3 -3
  77. package/dist/snippet.d.ts +7 -1
  78. package/dist/snippet.js +65 -8
  79. package/dist/surround-constructs.d.ts +10 -0
  80. package/dist/surround-constructs.js +100 -0
  81. package/dist/surround.d.ts +18 -0
  82. package/dist/surround.js +399 -0
  83. package/dist/symbol-picker.js +4 -6
  84. package/dist/symbols.js +22 -3
  85. package/dist/template-builtins.d.ts +3 -0
  86. package/dist/template-builtins.js +46 -0
  87. package/dist/template-context.d.ts +12 -0
  88. package/dist/template-context.js +56 -0
  89. package/dist/templates.d.ts +59 -0
  90. package/dist/templates.js +103 -0
  91. package/dist/testing.d.ts +23 -1
  92. package/dist/testing.js +114 -0
  93. package/package.json +7 -6
  94. package/src/CallHierarchyPanel.tsx +26 -11
  95. package/src/EditorContextMenu.tsx +7 -0
  96. package/src/LanguagePopups.tsx +4 -0
  97. package/src/PickPopup.tsx +47 -5
  98. package/src/WrapCard.tsx +64 -0
  99. package/src/call-hierarchy-model.ts +25 -3
  100. package/src/call-hierarchy.ts +62 -20
  101. package/src/clipboard-history.ts +102 -0
  102. package/src/code-actions.ts +48 -11
  103. package/src/code-vision.ts +3 -3
  104. package/src/completion.ts +123 -25
  105. package/src/context-menu-rows.ts +3 -2
  106. package/src/definition-link.ts +17 -5
  107. package/src/document-colors.ts +189 -0
  108. package/src/document-links.ts +129 -0
  109. package/src/document-query.ts +71 -0
  110. package/src/edit-locations.ts +162 -0
  111. package/src/editor-language.ts +86 -3
  112. package/src/emmet-module.d.ts +44 -0
  113. package/src/emmet.ts +73 -0
  114. package/src/folding.ts +3 -3
  115. package/src/format-on-type.ts +5 -5
  116. package/src/highlights.ts +3 -3
  117. package/src/history.ts +81 -1
  118. package/src/host-types.ts +17 -0
  119. package/src/index.ts +28 -3
  120. package/src/inlay-hints.ts +2 -2
  121. package/src/inline-completion.ts +221 -0
  122. package/src/key-priority.ts +11 -6
  123. package/src/linked-editing.ts +199 -0
  124. package/src/live-templates.ts +315 -0
  125. package/src/locales/en.json +61 -2
  126. package/src/locales/nl.json +61 -2
  127. package/src/markup-context.ts +387 -0
  128. package/src/markup.ts +377 -0
  129. package/src/navigation.ts +6 -6
  130. package/src/paste-history.ts +111 -0
  131. package/src/peek.ts +4 -4
  132. package/src/pick.ts +218 -20
  133. package/src/popups.ts +21 -1
  134. package/src/postfix.ts +224 -0
  135. package/src/project-language.ts +139 -19
  136. package/src/rename.ts +11 -11
  137. package/src/rendered-docs.ts +2 -2
  138. package/src/selection-ranges.ts +3 -3
  139. package/src/signature.ts +6 -6
  140. package/src/snippet-insert.ts +258 -0
  141. package/src/snippet-session.ts +9 -4
  142. package/src/snippet.ts +82 -7
  143. package/src/surround-constructs.ts +85 -0
  144. package/src/surround.ts +430 -0
  145. package/src/symbol-picker.ts +6 -6
  146. package/src/symbols.ts +30 -4
  147. package/src/template-builtins.ts +94 -0
  148. package/src/template-context.ts +65 -0
  149. package/src/templates.ts +197 -0
  150. package/src/testing.ts +126 -1
@@ -9,6 +9,7 @@ export declare class CallHierarchyFeature {
9
9
  private nodes;
10
10
  private roots;
11
11
  private prepared;
12
+ private position;
12
13
  private direction;
13
14
  private controller;
14
15
  private epoch;
@@ -17,6 +18,7 @@ export declare class CallHierarchyFeature {
17
18
  constructor(language: EditorLanguage, timers?: Timers);
18
19
  get isOpen(): boolean;
19
20
  get supported(): boolean;
21
+ get typesSupported(): boolean;
20
22
  open(position?: EditorPosition, direction?: CallDirection): Promise<void>;
21
23
  close(): void;
22
24
  setDirection(direction: CallDirection): void;
@@ -31,6 +33,7 @@ export declare class CallHierarchyFeature {
31
33
  private mounted;
32
34
  private patch;
33
35
  private load;
36
+ private childrenOf;
34
37
  private preview;
35
38
  private hasAncestor;
36
39
  private key;
@@ -1,6 +1,6 @@
1
1
  import { StaleResultError } from '@adecore/lsp';
2
2
  import { KEY_PRIORITY } from './key-priority.js';
3
- import { CALL_LEVEL_LIMIT, childrenOf, moreIdOf, rootsOf, rowsOf, statusIdOf, stepsOf, targetOf } from './call-hierarchy-model.js';
3
+ import { CALL_LEVEL_LIMIT, childrenOf, isTypeDirection, moreIdOf, rootsOf, rowsOf, statusIdOf, stepsOf, targetOf, typeChildrenOf } from './call-hierarchy-model.js';
4
4
  import { PEEK_HEIGHT } from './peek.js';
5
5
  import { snippetOf } from './peek-model.js';
6
6
  import { isShortcut } from './shortcut-keys.js';
@@ -8,6 +8,7 @@ import { messageOf } from './error-message.js';
8
8
  import { realTimers, revealRow } from './timers.js';
9
9
  const TOAST_ID = 'language-call-hierarchy';
10
10
  const PREPARE = 'textDocument/prepareCallHierarchy';
11
+ const PREPARE_TYPES = 'textDocument/prepareTypeHierarchy';
11
12
  /* The row the panel stands in is as tall as a peek's, plus the segmented control that is a pixel taller than the peek's title bar. */
12
13
  export const CALL_HIERARCHY_HEIGHT = PEEK_HEIGHT + 2;
13
14
  const OWNER = 'call-hierarchy';
@@ -24,6 +25,7 @@ export class CallHierarchyFeature {
24
25
  nodes = new Map();
25
26
  roots = [];
26
27
  prepared = [];
28
+ position = null;
27
29
  direction = 'incoming';
28
30
  controller = new AbortController();
29
31
  /* Raised whenever the tree starts over or closes, so an answer for the tree that was never lands in the one that is. */
@@ -37,11 +39,13 @@ export class CallHierarchyFeature {
37
39
  editor
38
40
  } = language;
39
41
  const offs = [language.onKey(event => {
40
- if (isShortcut(this.language.shortcuts.callHierarchy, event)) {
41
- if (this.isOpen) {
42
- this.setDirection(this.direction === 'incoming' ? 'outgoing' : 'incoming');
42
+ const calls = isShortcut(this.language.shortcuts.callHierarchy, event);
43
+ if (calls || isShortcut(this.language.shortcuts.typeHierarchy, event)) {
44
+ const [first, second] = calls ? ['incoming', 'outgoing'] : ['supertypes', 'subtypes'];
45
+ if (this.isOpen && isTypeDirection(this.direction) !== calls) {
46
+ this.setDirection(this.direction === first ? second : first);
43
47
  } else {
44
- void this.open();
48
+ void this.open(undefined, first);
45
49
  }
46
50
  return true;
47
51
  }
@@ -60,35 +64,45 @@ export class CallHierarchyFeature {
60
64
  /* Whether a server of this document answers the three requests the tree is made of. */
61
65
  get supported() {
62
66
  const {
63
- project,
67
+ service,
64
68
  uri
65
69
  } = this.language;
66
- const {
67
- service
68
- } = project;
69
70
  return service.prepareCallHierarchy !== undefined && service.incomingCalls !== undefined && service.outgoingCalls !== undefined && service.supports(PREPARE, uri);
70
71
  }
71
- /* Opens the tree for the symbol at the position, on its callers unless told otherwise. */
72
+ /* Whether a server of this document answers the three requests of the type hierarchy. */
73
+ get typesSupported() {
74
+ const {
75
+ service,
76
+ uri
77
+ } = this.language;
78
+ return service.prepareTypeHierarchy !== undefined && service.supertypes !== undefined && service.subtypes !== undefined && service.supports(PREPARE_TYPES, uri);
79
+ }
80
+ /*
81
+ * Opens the tree for the symbol at the position, on its callers unless told otherwise; `supertypes` or `subtypes`
82
+ * open the type hierarchy of the type at the position.
83
+ */
72
84
  async open(position = this.language.editor.getCaret(), direction = 'incoming') {
73
85
  const {
74
86
  editor,
75
- project,
87
+ service,
76
88
  uri
77
89
  } = this.language;
78
- if (!this.supported) {
79
- this.tell(this.say('unavailable'));
90
+ const types = isTypeDirection(direction);
91
+ if (!(types ? this.typesSupported : this.supported)) {
92
+ this.tell(this.say(types ? 'typesUnavailable' : 'unavailable'));
80
93
  return;
81
94
  }
82
95
  this.close();
83
96
  const epoch = this.epoch;
84
97
  let items;
85
98
  try {
86
- items = await project.service.prepareCallHierarchy(uri, position, {
99
+ const options = {
87
100
  signal: this.controller.signal
88
- });
101
+ };
102
+ items = types ? await service.prepareTypeHierarchy(uri, position, options) : await service.prepareCallHierarchy(uri, position, options);
89
103
  } catch (error) {
90
104
  if (epoch === this.epoch && !(error instanceof StaleResultError)) {
91
- this.tell(this.say('failed', {
105
+ this.tell(this.say(types ? 'typesFailed' : 'failed', {
92
106
  message: messageOf(error)
93
107
  }));
94
108
  }
@@ -98,11 +112,12 @@ export class CallHierarchyFeature {
98
112
  return;
99
113
  }
100
114
  if (items === null || items.length === 0) {
101
- this.tell(this.say('none'));
115
+ this.tell(this.say(types ? 'typesNone' : 'none'));
102
116
  return;
103
117
  }
104
118
  this.language.peek.close();
105
119
  this.prepared = items;
120
+ this.position = position;
106
121
  this.direction = direction;
107
122
  this.seed();
108
123
  const active = this.roots[0];
@@ -143,12 +158,16 @@ export class CallHierarchyFeature {
143
158
  this.language.editor.setInputPopup(null);
144
159
  this.language.editor.focus();
145
160
  }
146
- /* Starts the tree over from the same symbols for the other direction. */
161
+ /* Starts the tree over from the same symbols for the other direction; a switch between calls and types prepares the symbol again. */
147
162
  setDirection(direction) {
148
163
  const view = this.language.popups.getState().callHierarchy;
149
164
  if (view === null || view.direction === direction) {
150
165
  return;
151
166
  }
167
+ if (isTypeDirection(direction) !== isTypeDirection(view.direction)) {
168
+ void this.open(this.position ?? undefined, direction);
169
+ return;
170
+ }
152
171
  this.reset();
153
172
  this.direction = direction;
154
173
  this.seed();
@@ -309,11 +328,9 @@ export class CallHierarchyFeature {
309
328
  return;
310
329
  }
311
330
  const {
312
- service,
313
331
  i18n
314
332
  } = this.language.project;
315
333
  const {
316
- uri,
317
334
  editor
318
335
  } = this.language;
319
336
  const {
@@ -329,11 +346,10 @@ export class CallHierarchyFeature {
329
346
  error: null
330
347
  });
331
348
  try {
332
- const calls = direction === 'incoming' ? await service.incomingCalls(uri, node.item, options) : await service.outgoingCalls(uri, node.item, options);
349
+ const children = await this.childrenOf(direction, node, options);
333
350
  if (epoch !== this.epoch) {
334
351
  return;
335
352
  }
336
- const children = childrenOf(direction, node, calls ?? []);
337
353
  for (const child of children) {
338
354
  this.nodes.set(child.id, child);
339
355
  }
@@ -360,6 +376,22 @@ export class CallHierarchyFeature {
360
376
  });
361
377
  }
362
378
  }
379
+ async childrenOf(direction, node, options) {
380
+ const {
381
+ service,
382
+ uri
383
+ } = this.language;
384
+ switch (direction) {
385
+ case 'incoming':
386
+ return childrenOf(direction, node, (await service.incomingCalls(uri, node.item, options)) ?? []);
387
+ case 'outgoing':
388
+ return childrenOf(direction, node, (await service.outgoingCalls(uri, node.item, options)) ?? []);
389
+ case 'supertypes':
390
+ return typeChildrenOf(node, (await service.supertypes(uri, node.item, options)) ?? []);
391
+ case 'subtypes':
392
+ return typeChildrenOf(node, (await service.subtypes(uri, node.item, options)) ?? []);
393
+ }
394
+ }
363
395
  async preview(node, site) {
364
396
  const {
365
397
  project
@@ -0,0 +1,31 @@
1
+ export interface ClipboardEntry {
2
+ readonly text: string;
3
+ readonly wholeLines: boolean;
4
+ readonly at: number;
5
+ }
6
+ export interface ClipboardHistoryOptions {
7
+ readonly limit?: number;
8
+ readonly maxLength?: number;
9
+ readonly entries?: readonly ClipboardEntry[];
10
+ onChange?(entries: readonly ClipboardEntry[]): void;
11
+ readonly clock?: () => number;
12
+ }
13
+ export declare class ClipboardHistory {
14
+ private readonly limit;
15
+ private readonly maxLength;
16
+ private readonly onChange;
17
+ private readonly clock;
18
+ private readonly listeners;
19
+ private list;
20
+ constructor(options?: ClipboardHistoryOptions);
21
+ get entries(): readonly ClipboardEntry[];
22
+ add(text: string, options?: {
23
+ readonly wholeLines?: boolean;
24
+ }): void;
25
+ remove(entry: ClipboardEntry): void;
26
+ promote(entry: ClipboardEntry): void;
27
+ clear(): void;
28
+ subscribe(listener: () => void): () => void;
29
+ private change;
30
+ private trimmed;
31
+ }
@@ -0,0 +1,70 @@
1
+ /* The texts copied or cut in the editors of a project, newest first, for Paste from History. */
2
+ export class ClipboardHistory {
3
+ limit;
4
+ maxLength;
5
+ onChange;
6
+ clock;
7
+ listeners = new Set();
8
+ list;
9
+ constructor(options = {}) {
10
+ this.limit = options.limit ?? 100;
11
+ this.maxLength = options.maxLength ?? 10_000_000;
12
+ this.onChange = options.onChange;
13
+ this.clock = options.clock ?? Date.now;
14
+ this.list = this.trimmed(options.entries ?? []);
15
+ }
16
+ /* Newest first. */
17
+ get entries() {
18
+ return this.list;
19
+ }
20
+ /* Puts a copy on top. The same text again moves up instead of being kept twice, and nothing is kept for an empty text. */
21
+ add(text, options = {}) {
22
+ if (text === '') {
23
+ return;
24
+ }
25
+ const entry = {
26
+ text,
27
+ wholeLines: options.wholeLines === true,
28
+ at: this.clock()
29
+ };
30
+ this.change([entry, ...this.list.filter(other => other.text !== text)]);
31
+ }
32
+ remove(entry) {
33
+ if (this.list.includes(entry)) {
34
+ this.change(this.list.filter(other => other !== entry));
35
+ }
36
+ }
37
+ /* Moves an entry to the top, as choosing it does. */
38
+ promote(entry) {
39
+ if (this.list.includes(entry) && this.list[0] !== entry) {
40
+ this.change([entry, ...this.list.filter(other => other !== entry)]);
41
+ }
42
+ }
43
+ clear() {
44
+ if (this.list.length > 0) {
45
+ this.change([]);
46
+ }
47
+ }
48
+ subscribe(listener) {
49
+ this.listeners.add(listener);
50
+ return () => this.listeners.delete(listener);
51
+ }
52
+ change(entries) {
53
+ this.list = this.trimmed(entries);
54
+ this.onChange?.(this.list);
55
+ this.listeners.forEach(listener => listener());
56
+ }
57
+ /* Keeps the newest entries that fit the limit and the total length. */
58
+ trimmed(entries) {
59
+ const kept = [];
60
+ let length = 0;
61
+ for (const entry of entries.slice(0, this.limit)) {
62
+ length += entry.text.length;
63
+ if (kept.length > 0 && length > this.maxLength) {
64
+ break;
65
+ }
66
+ kept.push(entry);
67
+ }
68
+ return kept;
69
+ }
70
+ }
@@ -1,5 +1,5 @@
1
1
  import { type ActionEntry } from './code-actions-model.ts';
2
- import type { Problem } from './diagnostics-model.ts';
2
+ import { type Problem } from './diagnostics-model.ts';
3
3
  import type { EditorLanguage } from './editor-language.ts';
4
4
  import { type Timers } from './timers.ts';
5
5
  export declare class CodeActionsFeature {
@@ -14,6 +14,8 @@ export declare class CodeActionsFeature {
14
14
  quickFixFor(problem: Problem): Promise<void>;
15
15
  organizeImports(): Promise<void>;
16
16
  formatDocument(): Promise<void>;
17
+ get formatsSelection(): boolean;
18
+ formatSelection(): Promise<void>;
17
19
  list(only: readonly string[]): Promise<ActionEntry[] | null>;
18
20
  private ask;
19
21
  private request;
@@ -1,6 +1,7 @@
1
1
  import { ErrorCodes, LspError, StaleResultError } from '@adecore/lsp';
2
2
  import { KEY_PRIORITY } from './key-priority.js';
3
3
  import { ACTION_GROUPS, REFACTOR_GROUPS, actionsOf, diagnosticsAt, fixableOnLine, isHint, mergeEntries, previewOf } from './code-actions-model.js';
4
+ import { comparePositions } from './diagnostics-model.js';
4
5
  import { Refresher } from './refresher.js';
5
6
  import { isShortcut } from './shortcut-keys.js';
6
7
  import { realTimers } from './timers.js';
@@ -64,7 +65,7 @@ export class CodeActionsFeature {
64
65
  });
65
66
  }
66
67
  get supported() {
67
- return this.language.project.service.supports(METHOD, this.language.uri);
68
+ return this.language.service.supports(METHOD, this.language.uri);
68
69
  }
69
70
  /* The list under the caret, or under the selection's start. */
70
71
  open() {
@@ -121,15 +122,14 @@ export class CodeActionsFeature {
121
122
  async formatDocument() {
122
123
  const {
123
124
  editor,
124
- project,
125
125
  uri
126
126
  } = this.language;
127
- if (!project.service.supports('textDocument/formatting', uri)) {
127
+ if (!this.language.service.supports('textDocument/formatting', uri)) {
128
128
  this.tell('error', this.say('noFormatter'));
129
129
  return;
130
130
  }
131
131
  try {
132
- const edits = await project.service.formatting(uri, {
132
+ const edits = await this.language.service.formatting(uri, {
133
133
  ...editor.getIndentation()
134
134
  });
135
135
  if (edits !== null && edits.length > 0 && !editor.applyEdits(edits.map(edit => ({
@@ -142,6 +142,48 @@ export class CodeActionsFeature {
142
142
  this.fail(error);
143
143
  }
144
144
  }
145
+ /* Whether a server formats a part of this document, so `formatSelection` has something to ask. */
146
+ get formatsSelection() {
147
+ return this.language.service.supports('textDocument/rangeFormatting', this.language.uri);
148
+ }
149
+ /*
150
+ * Formats what is selected, every selection in one request when the server formats several ranges at once and
151
+ * one by one otherwise; the whole document when nothing is selected.
152
+ */
153
+ async formatSelection() {
154
+ const {
155
+ editor,
156
+ uri
157
+ } = this.language;
158
+ const ranges = editor.getSelections().filter(range => comparePositions(range.start, range.end) !== 0);
159
+ if (ranges.length === 0) {
160
+ await this.formatDocument();
161
+ return;
162
+ }
163
+ if (!this.formatsSelection) {
164
+ this.tell('error', this.say('noFormatter'));
165
+ return;
166
+ }
167
+ const {
168
+ service
169
+ } = this.language;
170
+ const formatting = {
171
+ ...editor.getIndentation()
172
+ };
173
+ try {
174
+ const edits = service.rangesFormatting !== undefined && service.supports('textDocument/rangesFormatting', uri) ? await service.rangesFormatting(uri, ranges, formatting) : (await Promise.all(ranges.map(range => service.rangeFormatting(uri, range, formatting, {
175
+ parallel: true
176
+ })))).flatMap(list => list ?? []);
177
+ if (edits !== null && edits.length > 0 && !editor.applyEdits(edits.map(edit => ({
178
+ range: edit.range,
179
+ text: edit.newText
180
+ })))) {
181
+ this.tell('error', this.say('readOnly'));
182
+ }
183
+ } catch (error) {
184
+ this.fail(error);
185
+ }
186
+ }
145
187
  /* The actions of one kind for the selection, as entries a menu can list and `apply` runs. */
146
188
  list(only) {
147
189
  if (!this.supported) {
@@ -206,7 +248,6 @@ export class CodeActionsFeature {
206
248
  /* Asks the servers; null when they could not answer, with the reason said aloud for a request a person made. */
207
249
  async request(range, triggerKind, options = {}) {
208
250
  const {
209
- project,
210
251
  uri,
211
252
  diagnostics
212
253
  } = this.language;
@@ -224,7 +265,7 @@ export class CodeActionsFeature {
224
265
  } : {}),
225
266
  triggerKind: triggerKind
226
267
  };
227
- const entries = actionsOf(await project.service.codeActions(uri, range, context, {
268
+ const entries = actionsOf(await this.language.service.codeActions(uri, range, context, {
228
269
  signal
229
270
  }), order);
230
271
  // A server may answer with more than the kinds asked for.
@@ -283,16 +324,15 @@ export class CodeActionsFeature {
283
324
  return known;
284
325
  }
285
326
  const {
286
- project,
287
327
  uri
288
328
  } = this.language;
289
329
  const {
290
330
  action
291
331
  } = entry;
292
332
  let result = action;
293
- if (action.edit === undefined && project.service.providerOptions(METHOD, uri)?.resolveProvider === true) {
333
+ if (action.edit === undefined && this.language.service.providerOptions(METHOD, uri)?.resolveProvider === true) {
294
334
  try {
295
- result = await project.service.resolveCodeAction(uri, action, {
335
+ result = await this.language.service.resolveCodeAction(uri, action, {
296
336
  signal
297
337
  });
298
338
  } catch (error) {
@@ -383,7 +423,7 @@ export class CodeActionsFeature {
383
423
  if (action.command?.command === INLINE_RENAME_COMMAND) {
384
424
  await this.startRename(action.command);
385
425
  } else if (action.command !== undefined) {
386
- await project.service.executeCommand(uri, action.command);
426
+ await this.language.service.executeCommand(uri, action.command);
387
427
  }
388
428
  } catch (error) {
389
429
  this.fail(error);
@@ -111,7 +111,7 @@ export class CodeVisionFeature {
111
111
  this.plan();
112
112
  }
113
113
  get canCount() {
114
- return this.settings.usages && this.language.project.service.supports(REFERENCES, this.language.uri);
114
+ return this.settings.usages && this.language.service.supports(REFERENCES, this.language.uri);
115
115
  }
116
116
  /* Queues the declarations in view whose count is not from the current text, nearest the top of the view first. */
117
117
  plan() {
@@ -128,14 +128,13 @@ export class CodeVisionFeature {
128
128
  }
129
129
  async count(declaration) {
130
130
  const {
131
- project,
132
131
  uri
133
132
  } = this.language;
134
133
  const controller = new AbortController();
135
134
  this.running.set(declaration.id, controller);
136
135
  const epoch = this.epoch;
137
136
  try {
138
- const locations = await project.service.references(uri, declaration.position, false, {
137
+ const locations = await this.language.service.references(uri, declaration.position, false, {
139
138
  signal: controller.signal,
140
139
  parallel: true
141
140
  });
@@ -1,5 +1,26 @@
1
+ import { type CompletionItem } from '@adecore/lsp';
2
+ import type { EditorPosition } from '@adecore/editor';
1
3
  import type { EditorLanguage } from './editor-language.ts';
2
4
  import { type Timers } from './timers.ts';
5
+ /** A row a source of the editor offers beside the language server's. */
6
+ export interface LocalCompletionItem {
7
+ /** The label, kind, detail, documentation, filter text and sort text of the row; its edit range is the typed prefix on the caret's line, ending at the caret. */
8
+ readonly item: CompletionItem;
9
+ /** Runs instead of the insertion: the source inserts the row itself. */
10
+ accept(): void;
11
+ }
12
+ /** What a source is asked for when the list is requested. */
13
+ export interface CompletionRequest {
14
+ readonly position: EditorPosition;
15
+ /** Whether the person asked for the list with the keyboard. */
16
+ readonly explicit: boolean;
17
+ readonly triggerCharacter?: string;
18
+ }
19
+ /** Rows that come from the editor itself, such as templates, merged into the list of the language server. */
20
+ export interface CompletionSource {
21
+ /** Synchronous and cheap; asked on every request of the list. */
22
+ items(request: CompletionRequest): readonly LocalCompletionItem[];
23
+ }
3
24
  export declare class CompletionFeature {
4
25
  private readonly language;
5
26
  private readonly timers;
@@ -24,9 +45,16 @@ export declare class CompletionFeature {
24
45
  private acceptSequence;
25
46
  private disposed;
26
47
  private readonly resolved;
48
+ private readonly sources;
49
+ private locals;
50
+ private shown;
27
51
  constructor(language: EditorLanguage, timers?: Timers);
28
52
  get isOpen(): boolean;
29
53
  invoke(): void;
54
+ /** Adds rows to every list, also in a document without a language server. Returns the function that takes the source away again. */
55
+ addSource(source: CompletionSource): () => void;
56
+ /** Opens the list with only these rows, the typed prefix starting at `start`, as Insert Live Template does. */
57
+ showItems(items: readonly LocalCompletionItem[], start: EditorPosition): void;
30
58
  move(delta: number): void;
31
59
  accept(replace: boolean, index?: number, commit?: string): Promise<void>;
32
60
  private insert;