@adecore/editor-react 0.42.0 → 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 (122) 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 +190 -70
  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.js +10 -15
  17. package/dist/context-menu-rows.d.ts +1 -1
  18. package/dist/context-menu-rows.js +4 -0
  19. package/dist/definition-link.js +15 -5
  20. package/dist/document-colors.d.ts +24 -0
  21. package/dist/document-colors.js +194 -0
  22. package/dist/document-links.d.ts +22 -0
  23. package/dist/document-links.js +133 -0
  24. package/dist/document-query.d.ts +15 -0
  25. package/dist/document-query.js +54 -0
  26. package/dist/edit-locations.d.ts +20 -0
  27. package/dist/edit-locations.js +136 -0
  28. package/dist/editor-language.d.ts +19 -1
  29. package/dist/editor-language.js +77 -2
  30. package/dist/emmet.d.ts +13 -0
  31. package/dist/emmet.js +62 -0
  32. package/dist/folding.js +2 -3
  33. package/dist/format-on-type.js +3 -5
  34. package/dist/highlights.js +2 -3
  35. package/dist/history.d.ts +7 -0
  36. package/dist/history.js +79 -1
  37. package/dist/host-types.d.ts +6 -0
  38. package/dist/index.d.ts +11 -2
  39. package/dist/index.js +8 -0
  40. package/dist/inlay-hints.js +2 -2
  41. package/dist/inline-completion.d.ts +39 -0
  42. package/dist/inline-completion.js +221 -0
  43. package/dist/key-priority.d.ts +8 -5
  44. package/dist/key-priority.js +9 -6
  45. package/dist/linked-editing.d.ts +26 -0
  46. package/dist/linked-editing.js +215 -0
  47. package/dist/locales/en.json +31 -2
  48. package/dist/locales/nl.json +31 -2
  49. package/dist/markup-context.d.ts +37 -0
  50. package/dist/markup-context.js +291 -0
  51. package/dist/markup.d.ts +36 -0
  52. package/dist/markup.js +453 -0
  53. package/dist/navigation.js +4 -6
  54. package/dist/paste-history.d.ts +7 -0
  55. package/dist/paste-history.js +104 -0
  56. package/dist/peek.js +3 -4
  57. package/dist/pick.d.ts +20 -2
  58. package/dist/pick.js +141 -18
  59. package/dist/popups.d.ts +9 -0
  60. package/dist/popups.js +2 -1
  61. package/dist/project-language.d.ts +18 -1
  62. package/dist/project-language.js +129 -18
  63. package/dist/rename.js +8 -11
  64. package/dist/rendered-docs.js +1 -2
  65. package/dist/selection-ranges.js +2 -3
  66. package/dist/shortcut-keys.d.ts +19 -0
  67. package/dist/signature.js +3 -6
  68. package/dist/snippet-session.d.ts +2 -2
  69. package/dist/snippet-session.js +3 -3
  70. package/dist/symbol-picker.js +4 -6
  71. package/dist/symbols.js +22 -3
  72. package/dist/testing.d.ts +23 -1
  73. package/dist/testing.js +114 -0
  74. package/package.json +7 -6
  75. package/src/CallHierarchyPanel.tsx +26 -11
  76. package/src/EditorContextMenu.tsx +7 -0
  77. package/src/LanguagePopups.tsx +4 -0
  78. package/src/PickPopup.tsx +40 -5
  79. package/src/WrapCard.tsx +64 -0
  80. package/src/call-hierarchy-model.ts +25 -3
  81. package/src/call-hierarchy.ts +62 -20
  82. package/src/clipboard-history.ts +102 -0
  83. package/src/code-actions.ts +48 -11
  84. package/src/code-vision.ts +3 -3
  85. package/src/completion.ts +15 -15
  86. package/src/context-menu-rows.ts +3 -2
  87. package/src/definition-link.ts +17 -5
  88. package/src/document-colors.ts +189 -0
  89. package/src/document-links.ts +129 -0
  90. package/src/document-query.ts +71 -0
  91. package/src/edit-locations.ts +162 -0
  92. package/src/editor-language.ts +80 -3
  93. package/src/emmet-module.d.ts +44 -0
  94. package/src/emmet.ts +73 -0
  95. package/src/folding.ts +3 -3
  96. package/src/format-on-type.ts +5 -5
  97. package/src/highlights.ts +3 -3
  98. package/src/history.ts +81 -1
  99. package/src/host-types.ts +10 -0
  100. package/src/index.ts +12 -3
  101. package/src/inlay-hints.ts +2 -2
  102. package/src/inline-completion.ts +221 -0
  103. package/src/key-priority.ts +9 -6
  104. package/src/linked-editing.ts +199 -0
  105. package/src/locales/en.json +31 -2
  106. package/src/locales/nl.json +31 -2
  107. package/src/markup-context.ts +262 -0
  108. package/src/markup.ts +367 -0
  109. package/src/navigation.ts +6 -6
  110. package/src/paste-history.ts +111 -0
  111. package/src/peek.ts +4 -4
  112. package/src/pick.ts +201 -19
  113. package/src/popups.ts +19 -1
  114. package/src/project-language.ts +136 -19
  115. package/src/rename.ts +11 -11
  116. package/src/rendered-docs.ts +2 -2
  117. package/src/selection-ranges.ts +3 -3
  118. package/src/signature.ts +6 -6
  119. package/src/snippet-session.ts +9 -4
  120. package/src/symbol-picker.ts +6 -6
  121. package/src/symbols.ts +30 -4
  122. package/src/testing.ts +126 -1
@@ -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
  });
@@ -198,7 +198,7 @@ export class CompletionFeature {
198
198
  };
199
199
  recentChoicesOf(this.language.languageId).record(entry.item);
200
200
  let item = this.resolved.get(entry.item) ?? entry.item;
201
- const needsResolve = !this.resolved.has(entry.item) && project.service.providerOptions('completionItem/resolve', uri) !== undefined;
201
+ const needsResolve = !this.resolved.has(entry.item) && this.language.service.providerOptions('completionItem/resolve', uri) !== undefined;
202
202
  this.close();
203
203
  if (needsResolve) {
204
204
  // Resolving can add the import an item needs; an item that cannot be resolved is still inserted as it is.
@@ -482,7 +482,6 @@ export class CompletionFeature {
482
482
  afterInsert(item, commit, lineBefore) {
483
483
  const {
484
484
  editor,
485
- project,
486
485
  uri,
487
486
  signature
488
487
  } = this.language;
@@ -508,7 +507,7 @@ export class CompletionFeature {
508
507
  if (command.command === PARAMETER_HINTS_COMMAND) {
509
508
  signature.trigger();
510
509
  } else {
511
- project.service.executeCommand(uri, command).catch(() => undefined);
510
+ this.language.service.executeCommand(uri, command).catch(() => undefined);
512
511
  }
513
512
  }
514
513
  /*
@@ -519,13 +518,12 @@ export class CompletionFeature {
519
518
  async resolveNow(item, signal) {
520
519
  const {
521
520
  editor,
522
- project,
523
521
  uri
524
522
  } = this.language;
525
523
  try {
526
524
  return {
527
525
  ...item,
528
- ...(await project.service.resolveCompletion(uri, item, {
526
+ ...(await this.language.service.resolveCompletion(uri, item, {
529
527
  signal
530
528
  }))
531
529
  };
@@ -535,14 +533,14 @@ export class CompletionFeature {
535
533
  }
536
534
  }
537
535
  try {
538
- const fresh = itemsOf(await project.service.completion(uri, editor.getCaret(), {
536
+ const fresh = itemsOf(await this.language.service.completion(uri, editor.getCaret(), {
539
537
  triggerKind: 1
540
538
  }, {
541
539
  signal
542
540
  })).items.find(candidate => candidate.label === item.label && candidate.kind === item.kind);
543
541
  return fresh === undefined ? item : {
544
542
  ...fresh,
545
- ...(await project.service.resolveCompletion(uri, fresh, {
543
+ ...(await this.language.service.resolveCompletion(uri, fresh, {
546
544
  signal
547
545
  }).catch(() => fresh))
548
546
  };
@@ -672,17 +670,15 @@ export class CompletionFeature {
672
670
  }
673
671
  triggerCharacters() {
674
672
  const {
675
- project,
676
673
  uri
677
674
  } = this.language;
678
- return project.service.providerOptions(METHOD, uri)?.triggerCharacters ?? [];
675
+ return this.language.service.providerOptions(METHOD, uri)?.triggerCharacters ?? [];
679
676
  }
680
677
  beginRequest(context, delay) {
681
678
  const {
682
- project,
683
679
  uri
684
680
  } = this.language;
685
- if (!project.service.supports(METHOD, uri)) {
681
+ if (!this.language.service.supports(METHOD, uri)) {
686
682
  return;
687
683
  }
688
684
  this.timers.clear(this.requestTimer);
@@ -701,7 +697,7 @@ export class CompletionFeature {
701
697
  this.controller = controller;
702
698
  let normalized;
703
699
  try {
704
- const result = await project.service.completion(uri, position, context, {
700
+ const result = await this.language.service.completion(uri, position, context, {
705
701
  signal: controller.signal
706
702
  });
707
703
  if (request !== this.request || this.controller !== controller || controller.signal.aborted) {
@@ -805,20 +801,19 @@ export class CompletionFeature {
805
801
  }
806
802
  scheduleResolve() {
807
803
  const {
808
- project,
809
804
  uri
810
805
  } = this.language;
811
806
  this.timers.clear(this.resolveTimer);
812
807
  this.resolving?.abort();
813
808
  const entry = this.ranked[this.active];
814
- if (entry === undefined || this.resolved.has(entry.item) || project.service.providerOptions('completionItem/resolve', uri) === undefined) {
809
+ if (entry === undefined || this.resolved.has(entry.item) || this.language.service.providerOptions('completionItem/resolve', uri) === undefined) {
815
810
  return;
816
811
  }
817
812
  const request = this.request;
818
813
  this.resolveTimer = this.timers.set(() => {
819
814
  const controller = new AbortController();
820
815
  this.resolving = controller;
821
- void project.service.resolveCompletion(uri, entry.item, {
816
+ void this.language.service.resolveCompletion(uri, entry.item, {
822
817
  signal: controller.signal
823
818
  }).then(item => {
824
819
  this.resolved.set(entry.item, {
@@ -1,6 +1,6 @@
1
1
  import type { KeymapId } from '@adecore/editor/keymap';
2
2
  import type { NavigationKind } from './navigation.ts';
3
- export type MenuRowId = NavigationKind | 'peekReferences' | 'callHierarchy' | 'renameSymbol' | 'codeActions';
3
+ export type MenuRowId = NavigationKind | 'peekReferences' | 'callHierarchy' | 'typeHierarchy' | 'renameSymbol' | 'codeActions';
4
4
  export interface MenuRowSpec {
5
5
  readonly id: MenuRowId;
6
6
  readonly method: string;
@@ -24,6 +24,10 @@ const REFERENCES = [{
24
24
  id: 'callHierarchy',
25
25
  method: 'textDocument/prepareCallHierarchy',
26
26
  shortcut: 'callHierarchy'
27
+ }, {
28
+ id: 'typeHierarchy',
29
+ method: 'textDocument/prepareTypeHierarchy',
30
+ shortcut: 'typeHierarchy'
27
31
  }];
28
32
  const CHANGES = [{
29
33
  id: 'renameSymbol',
@@ -25,8 +25,8 @@ export function followModKey(apple, onHeld) {
25
25
  }
26
26
  /*
27
27
  * The name under the pointer drawn as a link while Cmd (Ctrl elsewhere) is held and a server knows where it
28
- * is defined, which is what a Mod+click on it follows. The editor only says where the pointer is, so the
29
- * key comes from the window.
28
+ * is defined, which is what a Mod+click on it follows. A document link under the pointer goes first. The
29
+ * editor only says where the pointer is, so the key comes from the window.
30
30
  */
31
31
  export class DefinitionLinkFeature {
32
32
  language;
@@ -71,13 +71,23 @@ export class DefinitionLinkFeature {
71
71
  const {
72
72
  pointer
73
73
  } = this;
74
- if (!this.held || pointer === null || !this.language.project.service.supports('textDocument/definition', this.language.uri)) {
74
+ const link = pointer === null ? null : this.language.links.rangeAt(pointer);
75
+ if (this.held && link !== null) {
76
+ const key = `link:${link.start.line}:${link.start.character}`;
77
+ if (key !== this.shownKey) {
78
+ this.clear();
79
+ this.shownKey = key;
80
+ this.drawn = true;
81
+ this.language.editor.setLink(link);
82
+ }
83
+ return;
84
+ }
85
+ if (!this.held || pointer === null || !this.language.service.supports('textDocument/definition', this.language.uri)) {
75
86
  this.clear();
76
87
  return;
77
88
  }
78
89
  const {
79
90
  editor,
80
- project,
81
91
  uri
82
92
  } = this.language;
83
93
  const line = lineTextOf(editor, pointer.line);
@@ -95,7 +105,7 @@ export class DefinitionLinkFeature {
95
105
  const request = ++this.request;
96
106
  const controller = new AbortController();
97
107
  this.lookup = controller;
98
- project.service.definition(uri, pointer, {
108
+ this.language.service.definition(uri, pointer, {
99
109
  signal: controller.signal
100
110
  }).then(result => {
101
111
  if (request === this.request && locationsOf(result).length > 0) {
@@ -0,0 +1,24 @@
1
+ import type { Color, ColorInformation, ColorPresentation, TextEdit } from '@adecore/lsp';
2
+ import type { EditorLanguage } from './editor-language.ts';
3
+ import { type Timers } from './timers.ts';
4
+ export declare function cssColorOf(color: Color): string;
5
+ export declare function colorOfHex(value: string): Color | null;
6
+ export declare function presentationEdits(presentation: ColorPresentation, information: ColorInformation): TextEdit[];
7
+ export declare class DocumentColorsFeature {
8
+ private readonly language;
9
+ private colors;
10
+ private readonly query;
11
+ private enabled;
12
+ constructor(language: EditorLanguage, timers?: Timers);
13
+ get supported(): boolean;
14
+ all(): readonly ColorInformation[];
15
+ configure(settings: {
16
+ readonly enabled?: boolean;
17
+ }): void;
18
+ presentations(information: ColorInformation, color?: Color): Promise<ColorPresentation[]>;
19
+ apply(presentation: ColorPresentation, information: ColorInformation): boolean;
20
+ private show;
21
+ private pressed;
22
+ private open;
23
+ private choose;
24
+ }