@adecore/editor-react 0.39.0 → 0.40.1

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 (154) hide show
  1. package/dist/CallHierarchyPanel.js +30 -55
  2. package/dist/CodeAuthorsCard.js +0 -1
  3. package/dist/CompletionPopup.js +150 -181
  4. package/dist/DiffView.d.ts +1 -1
  5. package/dist/DiffView.js +2 -29
  6. package/dist/PickPopup.js +54 -80
  7. package/dist/SplitDiffView.js +27 -57
  8. package/dist/SymbolPicker.js +2 -8
  9. package/dist/code-actions-model.js +2 -9
  10. package/dist/code-actions.js +10 -11
  11. package/dist/code-vision.js +2 -1
  12. package/dist/completion-model.js +2 -2
  13. package/dist/completion.js +13 -34
  14. package/dist/definition-link.d.ts +1 -0
  15. package/dist/definition-link.js +22 -18
  16. package/dist/diagnostics-model.d.ts +1 -0
  17. package/dist/diagnostics-model.js +3 -1
  18. package/dist/diagnostics.js +1 -2
  19. package/dist/diff-syntax.d.ts +1 -0
  20. package/dist/diff-syntax.js +31 -0
  21. package/dist/doc-comment-model.d.ts +1 -0
  22. package/dist/doc-comment-model.js +1 -1
  23. package/dist/folding.js +2 -3
  24. package/dist/format-on-type.js +6 -10
  25. package/dist/hover-content.d.ts +0 -2
  26. package/dist/hover-content.js +1 -9
  27. package/dist/inlay-hints.d.ts +0 -1
  28. package/dist/inlay-hints.js +11 -41
  29. package/dist/navigation.js +4 -14
  30. package/dist/peek.js +2 -12
  31. package/dist/popup-focus.d.ts +3 -1
  32. package/dist/popup-focus.js +33 -0
  33. package/dist/recent-choices.d.ts +0 -1
  34. package/dist/recent-choices.js +0 -4
  35. package/dist/rendered-docs.d.ts +1 -0
  36. package/dist/rendered-docs.js +15 -28
  37. package/dist/snippet-field-transforms.d.ts +5 -0
  38. package/dist/snippet-field-transforms.js +27 -16
  39. package/dist/snippet-session.js +8 -20
  40. package/dist/symbol-links.js +3 -10
  41. package/dist/symbol-picker-model.d.ts +1 -0
  42. package/dist/symbol-picker-model.js +7 -0
  43. package/dist/unique.d.ts +10 -0
  44. package/dist/unique.js +15 -0
  45. package/package.json +9 -9
  46. package/src/AnchoredPopup.tsx +2 -0
  47. package/src/BaselineStatus.tsx +1 -0
  48. package/src/CallHierarchyPanel.tsx +16 -6
  49. package/src/ChangeReview.tsx +7 -0
  50. package/src/CodeAuthorsCard.tsx +2 -5
  51. package/src/CompletionPopup.tsx +21 -15
  52. package/src/DiffView.tsx +53 -30
  53. package/src/EditorContextMenu.tsx +7 -0
  54. package/src/EditorView.tsx +28 -0
  55. package/src/FindReplace.tsx +8 -0
  56. package/src/HoverCard.tsx +8 -0
  57. package/src/HoverSections.tsx +11 -0
  58. package/src/LanguagePopups.tsx +1 -0
  59. package/src/PanelResizeHandle.tsx +8 -0
  60. package/src/PathText.tsx +1 -0
  61. package/src/PeekPanel.tsx +13 -0
  62. package/src/PickPopup.tsx +7 -6
  63. package/src/RenameCard.tsx +13 -0
  64. package/src/RenderedDocs.tsx +11 -0
  65. package/src/SplitDiffView.tsx +91 -56
  66. package/src/SymbolPicker.tsx +10 -18
  67. package/src/call-hierarchy-model.ts +10 -0
  68. package/src/call-hierarchy.ts +82 -0
  69. package/src/code-actions-model.ts +21 -9
  70. package/src/code-actions.ts +83 -7
  71. package/src/code-authors.ts +20 -0
  72. package/src/code-vision-model.ts +37 -0
  73. package/src/code-vision.ts +47 -3
  74. package/src/completion-call.ts +19 -0
  75. package/src/completion-model.ts +58 -2
  76. package/src/completion.ts +156 -30
  77. package/src/conflict-model.ts +4 -0
  78. package/src/context-menu-rows.ts +1 -0
  79. package/src/context-menu.ts +7 -0
  80. package/src/definition-link.ts +42 -18
  81. package/src/diagnostics-model.ts +19 -1
  82. package/src/diagnostics.ts +31 -2
  83. package/src/diff-expansion.ts +9 -0
  84. package/src/diff-overlays.tsx +8 -0
  85. package/src/diff-syntax.ts +54 -0
  86. package/src/diff-wordmarks.ts +8 -0
  87. package/src/doc-comment-model.ts +51 -1
  88. package/src/editor-language.ts +11 -0
  89. package/src/find-query.ts +7 -0
  90. package/src/folding.ts +10 -3
  91. package/src/format-on-type.ts +23 -10
  92. package/src/highlight-layers.ts +3 -0
  93. package/src/highlights.ts +18 -0
  94. package/src/history.ts +11 -0
  95. package/src/holder.ts +3 -0
  96. package/src/hover-content.ts +34 -17
  97. package/src/hover.ts +57 -0
  98. package/src/index.ts +4 -0
  99. package/src/inlay-hints.ts +86 -47
  100. package/src/key-priority.ts +2 -0
  101. package/src/line-edits.ts +8 -0
  102. package/src/models.ts +6 -0
  103. package/src/navigation-history.ts +5 -0
  104. package/src/navigation.ts +28 -13
  105. package/src/panel-size.ts +7 -0
  106. package/src/patch-decorations.ts +39 -0
  107. package/src/patch-layout.ts +47 -0
  108. package/src/peek-model.ts +21 -0
  109. package/src/peek.ts +49 -13
  110. package/src/pick.ts +35 -0
  111. package/src/popup-focus.ts +11 -1
  112. package/src/popup-placement.ts +5 -0
  113. package/src/popups.ts +2 -6
  114. package/src/project-language.ts +33 -0
  115. package/src/project-problems.ts +11 -0
  116. package/src/proposal-model.ts +40 -0
  117. package/src/recent-choices.ts +3 -5
  118. package/src/refresher.ts +3 -0
  119. package/src/rename-model.ts +28 -0
  120. package/src/rename.ts +56 -0
  121. package/src/rendered-docs.ts +89 -19
  122. package/src/review-group.ts +4 -0
  123. package/src/row-host.ts +12 -0
  124. package/src/selection-ranges.ts +6 -0
  125. package/src/semantic-delta.ts +7 -0
  126. package/src/semantic-model.ts +11 -0
  127. package/src/semantic-tokens.ts +37 -0
  128. package/src/shortcut-keys.ts +3 -0
  129. package/src/signature-model.ts +7 -0
  130. package/src/signature.ts +27 -0
  131. package/src/snippet-field-transforms.ts +48 -13
  132. package/src/snippet-indent.ts +10 -0
  133. package/src/snippet-range-order.ts +5 -0
  134. package/src/snippet-session.ts +173 -31
  135. package/src/snippet-transform-client.ts +14 -0
  136. package/src/snippet-transform-format.ts +98 -0
  137. package/src/snippet-transform-protocol.ts +5 -0
  138. package/src/snippet-transform-queue.ts +53 -0
  139. package/src/snippet-transform-worker.ts +14 -0
  140. package/src/snippet-variables.ts +9 -0
  141. package/src/snippet.ts +107 -0
  142. package/src/split-alignment.ts +10 -0
  143. package/src/split-scroll.ts +8 -0
  144. package/src/symbol-links.ts +9 -12
  145. package/src/symbol-picker-model.ts +25 -0
  146. package/src/symbol-picker.ts +9 -0
  147. package/src/symbols.ts +21 -0
  148. package/src/testing.ts +7 -0
  149. package/src/timers.ts +5 -0
  150. package/src/unique.ts +23 -0
  151. package/src/use-problem-counts.ts +4 -0
  152. package/src/view-state.ts +9 -0
  153. package/src/word-diff.ts +5 -0
  154. package/src/workspace-edit.ts +84 -0
package/dist/PickPopup.js CHANGED
@@ -2,7 +2,7 @@ import { c as _c } from "react/compiler-runtime";
2
2
  import { useId, useLayoutEffect, useRef } from 'react';
3
3
  import { createPortal } from 'react-dom';
4
4
  import { PathText } from "./PathText.js";
5
- import { keepFocusOutOfList } from './popup-focus.js';
5
+ import { keepFocusOutOfList, useInputPopup } from './popup-focus.js';
6
6
  import { placePopup } from './popup-placement.js';
7
7
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
8
  const CARD = 'fixed top-0 left-0 z-(--z-popup) flex w-[420px] flex-col overflow-hidden rounded-lg border border-border bg-surface-raised text-text shadow-(--float-shadow)';
@@ -95,7 +95,7 @@ function _temp(line, index) {
95
95
  }, `-${index}`);
96
96
  }
97
97
  export function PickPopup(t0) {
98
- const $ = _c(39);
98
+ const $ = _c(31);
99
99
  const {
100
100
  language,
101
101
  view,
@@ -175,64 +175,38 @@ export function PickPopup(t0) {
175
175
  t4 = $[10];
176
176
  }
177
177
  const activeId = t4;
178
+ useInputPopup(language, listId, activeId);
178
179
  let t5;
179
- if ($[11] !== activeId || $[12] !== language.editor || $[13] !== listId) {
180
- t5 = () => {
181
- language.editor.setInputPopup({
182
- controls: listId,
183
- activeDescendant: activeId
184
- });
185
- return () => language.editor.setInputPopup(null);
180
+ if ($[11] === Symbol.for("react.memo_cache_sentinel")) {
181
+ t5 = {
182
+ visibility: "hidden"
186
183
  };
187
- $[11] = activeId;
188
- $[12] = language.editor;
189
- $[13] = listId;
190
- $[14] = t5;
184
+ $[11] = t5;
191
185
  } else {
192
- t5 = $[14];
186
+ t5 = $[11];
193
187
  }
194
188
  let t6;
195
- if ($[15] !== activeId || $[16] !== language || $[17] !== listId) {
196
- t6 = [language, listId, activeId];
197
- $[15] = activeId;
198
- $[16] = language;
199
- $[17] = listId;
200
- $[18] = t6;
189
+ if ($[12] !== language) {
190
+ t6 = keepFocusOutOfList(() => language.editor.focus());
191
+ $[12] = language;
192
+ $[13] = t6;
201
193
  } else {
202
- t6 = $[18];
194
+ t6 = $[13];
203
195
  }
204
- useLayoutEffect(t5, t6);
205
196
  let t7;
206
- if ($[19] === Symbol.for("react.memo_cache_sentinel")) {
207
- t7 = {
208
- visibility: "hidden"
209
- };
210
- $[19] = t7;
211
- } else {
212
- t7 = $[19];
213
- }
214
- let t8;
215
- if ($[20] !== language.editor) {
216
- t8 = keepFocusOutOfList(() => language.editor.focus());
217
- $[20] = language.editor;
218
- $[21] = t8;
219
- } else {
220
- t8 = $[21];
221
- }
222
- let t9;
223
- if ($[22] !== view.title) {
224
- t9 = view.title !== null && /*#__PURE__*/_jsx("div", {
197
+ if ($[14] !== view.title) {
198
+ t7 = view.title !== null && /*#__PURE__*/_jsx("div", {
225
199
  className: "border-b border-border px-3 py-1.5 text-xs text-text-muted",
226
200
  children: view.title
227
201
  });
228
- $[22] = view.title;
229
- $[23] = t9;
202
+ $[14] = view.title;
203
+ $[15] = t7;
230
204
  } else {
231
- t9 = $[23];
205
+ t7 = $[15];
232
206
  }
233
- let t10;
234
- if ($[24] !== language.pick || $[25] !== optionIds || $[26] !== view.active || $[27] !== view.groups) {
235
- t10 = view.groups.map((group_0, groupIndex) => /*#__PURE__*/_jsxs("div", {
207
+ let t8;
208
+ if ($[16] !== language || $[17] !== optionIds || $[18] !== view.active || $[19] !== view.groups) {
209
+ t8 = view.groups.map((group_0, groupIndex) => /*#__PURE__*/_jsxs("div", {
236
210
  role: "group",
237
211
  className: "flex flex-col",
238
212
  children: [group_0.title !== null && /*#__PURE__*/_jsx("div", {
@@ -260,57 +234,57 @@ export function PickPopup(t0) {
260
234
  })]
261
235
  }, row_0.id))]
262
236
  }, group_0.title ?? groupIndex));
263
- $[24] = language.pick;
264
- $[25] = optionIds;
265
- $[26] = view.active;
266
- $[27] = view.groups;
267
- $[28] = t10;
237
+ $[16] = language;
238
+ $[17] = optionIds;
239
+ $[18] = view.active;
240
+ $[19] = view.groups;
241
+ $[20] = t8;
268
242
  } else {
269
- t10 = $[28];
243
+ t8 = $[20];
270
244
  }
271
- let t11;
272
- if ($[29] !== listId || $[30] !== t10) {
273
- t11 = /*#__PURE__*/_jsx("div", {
245
+ let t9;
246
+ if ($[21] !== listId || $[22] !== t8) {
247
+ t9 = /*#__PURE__*/_jsx("div", {
274
248
  id: listId,
275
249
  role: "listbox",
276
250
  className: "min-h-0 flex-1 overflow-y-auto p-1",
277
- children: t10
251
+ children: t8
278
252
  });
279
- $[29] = listId;
280
- $[30] = t10;
281
- $[31] = t11;
253
+ $[21] = listId;
254
+ $[22] = t8;
255
+ $[23] = t9;
282
256
  } else {
283
- t11 = $[31];
257
+ t9 = $[23];
284
258
  }
285
- let t12;
286
- if ($[32] !== view.preview) {
287
- t12 = view.preview !== null && /*#__PURE__*/_jsx(PreviewLines, {
259
+ let t10;
260
+ if ($[24] !== view.preview) {
261
+ t10 = view.preview !== null && /*#__PURE__*/_jsx(PreviewLines, {
288
262
  preview: view.preview
289
263
  });
290
- $[32] = view.preview;
291
- $[33] = t12;
264
+ $[24] = view.preview;
265
+ $[25] = t10;
292
266
  } else {
293
- t12 = $[33];
267
+ t10 = $[25];
294
268
  }
295
- let t13;
296
- if ($[34] !== t11 || $[35] !== t12 || $[36] !== t8 || $[37] !== t9) {
297
- t13 = createPortal(/*#__PURE__*/_jsxs("div", {
269
+ let t11;
270
+ if ($[26] !== t10 || $[27] !== t6 || $[28] !== t7 || $[29] !== t9) {
271
+ t11 = createPortal(/*#__PURE__*/_jsxs("div", {
298
272
  ref: card,
299
273
  className: CARD,
300
- style: t7,
274
+ style: t5,
301
275
  onPointerDown: _temp3,
302
- ...t8,
303
- children: [t9, t11, t12]
276
+ ...t6,
277
+ children: [t7, t9, t10]
304
278
  }), document.body);
305
- $[34] = t11;
306
- $[35] = t12;
307
- $[36] = t8;
308
- $[37] = t9;
309
- $[38] = t13;
279
+ $[26] = t10;
280
+ $[27] = t6;
281
+ $[28] = t7;
282
+ $[29] = t9;
283
+ $[30] = t11;
310
284
  } else {
311
- t13 = $[38];
285
+ t11 = $[30];
312
286
  }
313
- return t13;
287
+ return t11;
314
288
  }
315
289
  function _temp3(event) {
316
290
  return event.preventDefault();
@@ -1,6 +1,7 @@
1
1
  import { useLayoutEffect, useMemo, useRef, useState } from 'react';
2
2
  import { useTranslation } from 'react-i18next';
3
3
  import { parseChord } from '@adecore/editor/keymap';
4
+ import { repaintOnTokens } from './diff-syntax.js';
4
5
  import { DiffOverlays } from "./diff-overlays.js";
5
6
  import { wordMarks } from './diff-wordmarks.js';
6
7
  import { RowHost, LineActionHost } from './row-host.js';
@@ -281,35 +282,7 @@ export function SplitDiffView({
281
282
  }
282
283
  };
283
284
  refresh();
284
- const ownerWindow = oldElement.current?.ownerDocument.defaultView;
285
- let frame;
286
- let pending_0 = false;
287
- let stopped_0 = false;
288
- const schedule = () => {
289
- if (pending_0) {
290
- return;
291
- }
292
- pending_0 = true;
293
- const paint = () => {
294
- pending_0 = false;
295
- if (!stopped_0) {
296
- refresh();
297
- }
298
- };
299
- if (ownerWindow?.requestAnimationFrame !== undefined) {
300
- frame = ownerWindow.requestAnimationFrame(paint);
301
- } else {
302
- queueMicrotask(paint);
303
- }
304
- };
305
- const stop_0 = tokens?.subscribe(schedule);
306
- return () => {
307
- stopped_0 = true;
308
- stop_0?.();
309
- if (frame !== undefined) {
310
- ownerWindow?.cancelAnimationFrame?.(frame);
311
- }
312
- };
285
+ return repaintOnTokens(oldElement.current?.ownerDocument.defaultView, tokens, refresh);
313
286
  }, [mounted, engine, display, tokens]);
314
287
  useLayoutEffect(() => {
315
288
  if (mounted === null || mounted.old.engine !== engine) {
@@ -317,8 +290,8 @@ export function SplitDiffView({
317
290
  }
318
291
  const stops_0 = SIDES.map(side_7 => onMount?.(mounted[side_7].editor, side_7));
319
292
  return () => {
320
- for (const stop_1 of stops_0) {
321
- stop_1?.();
293
+ for (const stop_0 of stops_0) {
294
+ stop_0?.();
322
295
  }
323
296
  };
324
297
  }, [mounted, engine, onMount]);
@@ -335,32 +308,29 @@ export function SplitDiffView({
335
308
  style: {
336
309
  height: fill ? '100%' : Math.min(height, limit)
337
310
  },
338
- children: SIDES.map(side_8 => /*#__PURE__*/_jsxs("div", {
339
- className: "adecore-editor-diff-side",
340
- "data-side": side_8,
341
- role: "group",
342
- "aria-label": t(`diff.${side_8}Side`),
343
- onWheelCapture: () => {
344
- mounted?.drive(side_8);
345
- },
346
- onFocusCapture: () => {
311
+ children: SIDES.map(side_8 => {
312
+ const drive = () => {
347
313
  mounted?.drive(side_8);
348
- },
349
- onPointerDownCapture: () => {
350
- mounted?.drive(side_8);
351
- },
352
- onKeyDownCapture: () => {
353
- mounted?.drive(side_8);
354
- },
355
- children: [/*#__PURE__*/_jsx("div", {
356
- ref: side_8 === 'old' ? oldElement : newElement,
357
- className: "adecore-editor-diff-surface"
358
- }), mounted !== null && mounted[side_8].engine === engine && /*#__PURE__*/_jsx(DiffOverlays, {
359
- mounted: mounted[side_8],
360
- display: display[side_8],
361
- unfold: unfold,
362
- blocked: blocked
363
- })]
364
- }, side_8))
314
+ };
315
+ return /*#__PURE__*/_jsxs("div", {
316
+ className: "adecore-editor-diff-side",
317
+ "data-side": side_8,
318
+ role: "group",
319
+ "aria-label": t(`diff.${side_8}Side`),
320
+ onWheelCapture: drive,
321
+ onFocusCapture: drive,
322
+ onPointerDownCapture: drive,
323
+ onKeyDownCapture: drive,
324
+ children: [/*#__PURE__*/_jsx("div", {
325
+ ref: side_8 === 'old' ? oldElement : newElement,
326
+ className: "adecore-editor-diff-surface"
327
+ }), mounted !== null && mounted[side_8].engine === engine && /*#__PURE__*/_jsx(DiffOverlays, {
328
+ mounted: mounted[side_8],
329
+ display: display[side_8],
330
+ unfold: unfold,
331
+ blocked: blocked
332
+ })]
333
+ }, side_8);
334
+ })
365
335
  });
366
336
  }
@@ -7,14 +7,8 @@ import { fileUriToPath } from '@adecore/lsp';
7
7
  import { formatShortcut, Icon } from '@adecore/ui';
8
8
  import { formatNumber } from '@adecore/ui/format';
9
9
  import { basenameOf } from './paths.js';
10
- import { filterEntries, groupEntries, letterOfKind, modeOf, toneOfKind } from './symbol-picker-model.js';
10
+ import { filterEntries, groupEntries, letterOfKind, modeOf, toneOfKind, TONE_CLASSES } from './symbol-picker-model.js';
11
11
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
- const TONES = {
13
- callable: 'bg-accent-soft text-accent',
14
- value: 'bg-status-running/15 text-status-running',
15
- type: 'bg-status-idle/15 text-status-idle',
16
- other: 'bg-surface-hover text-text-muted'
17
- };
18
12
  const SEARCH_DELAY_MS = 200;
19
13
  /* The letter badge of a symbol kind, tinted by what the symbol is. */
20
14
  export function SymbolBadge(t0) {
@@ -22,7 +16,7 @@ export function SymbolBadge(t0) {
22
16
  const {
23
17
  kind
24
18
  } = t0;
25
- const t1 = `grid size-4 shrink-0 place-items-center rounded-sm font-mono text-xs font-semibold ${TONES[toneOfKind(kind)]}`;
19
+ const t1 = `grid size-4 shrink-0 place-items-center rounded-sm font-mono text-xs font-semibold ${TONE_CLASSES[toneOfKind(kind)]}`;
26
20
  let t2;
27
21
  if ($[0] !== kind) {
28
22
  t2 = letterOfKind(kind);
@@ -1,5 +1,6 @@
1
1
  import { applyTextEdits } from '@adecore/lsp';
2
2
  import { comparePositions } from './diagnostics-model.js';
3
+ import { uniqueBy } from './unique.js';
3
4
  import { resourceOperationsOf, textEntriesOf } from './workspace-edit.js';
4
5
  /* The order of the list under the caret: the fixes and the rewrites that are one keystroke away first, the refactorings after them. */
5
6
  export const ACTION_GROUPS = ['quickfix', 'rewrite', 'extract', 'inline', 'move', 'refactor', 'source', 'other'];
@@ -47,15 +48,7 @@ export function actionsOf(result, order = ACTION_GROUPS) {
47
48
  }
48
49
  /* Entries from several requests as one list: the same order as a single answer, and an id of its own for each row. Two actions with a title and kind alike are one. */
49
50
  export function mergeEntries(lists) {
50
- const seen = new Set();
51
- const unique = lists.flat().filter(entry => {
52
- const key = `${entry.action.kind ?? ''}\0${entry.action.title}`;
53
- if (seen.has(key)) {
54
- return false;
55
- }
56
- seen.add(key);
57
- return true;
58
- });
51
+ const unique = uniqueBy(lists.flat(), entry => `${entry.action.kind ?? ''}\0${entry.action.title}`);
59
52
  return inGroupOrder(unique, ACTION_GROUPS).map((entry, index) => ({
60
53
  ...entry,
61
54
  id: String(index)
@@ -334,17 +334,16 @@ export class CodeActionsFeature {
334
334
  if (found === null) {
335
335
  return null;
336
336
  }
337
- const notes = [...(found.hiddenLines > 0 ? [this.say('moreLines', {
338
- count: found.hiddenLines
339
- })] : []), ...(found.otherFiles > 0 ? [this.say('otherFiles', {
340
- count: found.otherFiles
341
- })] : []), ...(found.moves > 0 ? [this.say('moves', {
342
- count: found.moves
343
- })] : []), ...((found.creates ?? 0) > 0 ? [this.say('creates', {
344
- count: found.creates
345
- })] : []), ...((found.deletes ?? 0) > 0 ? [this.say('deletes', {
346
- count: found.deletes
347
- })] : [])];
337
+ const counts = {
338
+ moreLines: found.hiddenLines,
339
+ otherFiles: found.otherFiles,
340
+ moves: found.moves,
341
+ creates: found.creates ?? 0,
342
+ deletes: found.deletes ?? 0
343
+ };
344
+ const notes = Object.entries(counts).filter(([, count]) => count > 0).map(([key, count]) => this.say(key, {
345
+ count
346
+ }));
348
347
  return {
349
348
  removed: found.removed,
350
349
  added: found.added,
@@ -2,6 +2,7 @@ import { splitLines } from '@adecore/merge';
2
2
  import { authorsText, authorshipOf, mapBlame } from './code-authors.js';
3
3
  import { declarationsOf, usagesText } from './code-vision-model.js';
4
4
  import { realTimers } from './timers.js';
5
+ import { placeKey } from './unique.js';
5
6
  const REFERENCES = 'textDocument/references';
6
7
  /* Lines asked about above and below what is in view, so a little scrolling stays inside what is counted. */
7
8
  const MARGIN_LINES = 40;
@@ -253,5 +254,5 @@ export class CodeVisionFeature {
253
254
  }
254
255
  /* The places a server named, each counted once however many ways it was reached. */
255
256
  function distinctPlaces(locations) {
256
- return new Set(locations.map(location => `${location.uri}\0${location.range.start.line}\0${location.range.start.character}`)).size;
257
+ return new Set(locations.map(placeKey)).size;
257
258
  }
@@ -1,6 +1,6 @@
1
1
  import { labelCollator } from '@adecore/ui/format';
2
2
  import { applyCompletionDefaults } from '@adecore/lsp';
3
- import { comparePositions } from './diagnostics-model.js';
3
+ import { comparePositions, sameRange } from './diagnostics-model.js';
4
4
  import { escapeMarkdown, isEmptyHover, splitSignatures } from './hover-content.js';
5
5
  import { compileSnippet, parseSnippet, tabOrder } from './snippet.js';
6
6
  import { indentSnippet } from './snippet-indent.js';
@@ -333,7 +333,7 @@ export function mirroredInsertions(carets, primary, caret, text, insertion, incl
333
333
  });
334
334
  return carets.flatMap(other => {
335
335
  const at = includeSelections ? other.end : other.start;
336
- if (comparePositions(other.start, primary.start) === 0 && comparePositions(other.end, primary.end) === 0) {
336
+ if (sameRange(other, primary)) {
337
337
  return [];
338
338
  }
339
339
  if (!includeSelections && comparePositions(other.start, other.end) !== 0 || at.character < before) {
@@ -2,8 +2,9 @@ import { SnippetTransformQueueError } from './snippet-transform-queue.js';
2
2
  import { KEY_PRIORITY } from './key-priority.js';
3
3
  import { SnippetTransformError } from './snippet-transform-client.js';
4
4
  import { MAX_TRANSFORM_JOBS, MAX_TRANSFORM_WORK } from './snippet-transform-protocol.js';
5
+ import { jobTable } from './snippet-field-transforms.js';
5
6
  import { fileUriToPath, StaleResultError } from '@adecore/lsp';
6
- import { comparePositions, shiftPosition } from './diagnostics-model.js';
7
+ import { comparePositions, sameRange, shiftPosition } from './diagnostics-model.js';
7
8
  import { completionDocsOf, editRangeOf, identifierPrefix, insertionOf, isIdentifierCharacter, itemsOf, matchedCharacters, mirroredInsertions, opensCompletion, prefixFor, qualifiersOf, rankCompletions } from './completion-model.js';
8
9
  import { PARAMETER_HINTS_COMMAND, isCallItem, planCall, withParentheses } from './completion-call.js';
9
10
  import { compileSnippet } from './snippet.js';
@@ -11,7 +12,7 @@ import { createSnippetResolver } from './snippet-variables.js';
11
12
  import { wordRangeAt } from './rename-model.js';
12
13
  import { RecentChoices, recentChoicesOf } from './recent-choices.js';
13
14
  import { shikiLanguageOf } from './language-ids.js';
14
- import { textBeforeOf } from './line-text.js';
15
+ import { lineTextOf, textBeforeOf } from './line-text.js';
15
16
  import { isEmptyRange } from './proposal-model.js';
16
17
  import { isShortcut } from './shortcut-keys.js';
17
18
  import { realTimers } from './timers.js';
@@ -193,7 +194,7 @@ export class CompletionFeature {
193
194
  return false;
194
195
  }
195
196
  const selections = editor.getSelections();
196
- return selections.length === acceptedSelections.length && selections.every((range, index) => comparePositions(range.start, acceptedSelections[index].start) === 0 && comparePositions(range.end, acceptedSelections[index].end) === 0);
197
+ return selections.length === acceptedSelections.length && selections.every((range, index) => sameRange(range, acceptedSelections[index]));
197
198
  };
198
199
  recentChoicesOf(this.language.languageId).record(entry.item);
199
200
  let item = this.resolved.get(entry.item) ?? entry.item;
@@ -234,19 +235,10 @@ export class CompletionFeature {
234
235
  let characters = 0;
235
236
  let ranges = 0;
236
237
  const now = new Date();
237
- const template = item.insertTextFormat === 2 ? compileSnippet(item.textEdit?.newText ?? item.insertText ?? item.label) : undefined;
238
+ const template = item.insertTextFormat === 2 ? compileSnippet(source) : undefined;
238
239
  const finishes = new Map();
239
240
  const renderAt = (position, selection) => {
240
- const line = editor.textInRange({
241
- start: {
242
- line: position.line,
243
- character: 0
244
- },
245
- end: {
246
- line: position.line,
247
- character: Number.MAX_SAFE_INTEGER
248
- }
249
- });
241
+ const line = lineTextOf(editor, position.line);
250
242
  const word = wordRangeAt(line, position);
251
243
  const resolver = createSnippetResolver({
252
244
  uri,
@@ -256,7 +248,7 @@ export class CompletionFeature {
256
248
  selectedText: editor.textInRange(selection),
257
249
  currentLine: line,
258
250
  currentWord: word === null ? '' : editor.textInRange(word),
259
- cursorIndex: Math.max(0, carets.findIndex(range => comparePositions(range.start, selection.start) === 0 && comparePositions(range.end, selection.end) === 0)),
251
+ cursorIndex: Math.max(0, carets.findIndex(range => sameRange(range, selection))),
260
252
  cursorCount: carets.length,
261
253
  folder: project.host.folder,
262
254
  path: fileUriToPath(uri) ?? project.host.pathOfUri?.(uri) ?? undefined
@@ -275,13 +267,7 @@ export class CompletionFeature {
275
267
  template,
276
268
  variableTransforms,
277
269
  resolve: resolver,
278
- indentationBefore: editStart === undefined ? undefined : editor.textInRange({
279
- start: {
280
- line: editStart.line,
281
- character: 0
282
- },
283
- end: editStart
284
- })
270
+ indentationBefore: editStart === undefined ? undefined : textBeforeOf(editor, editStart)
285
271
  });
286
272
  return commit === undefined ? insertion : {
287
273
  ...insertion,
@@ -344,18 +330,11 @@ export class CompletionFeature {
344
330
  return edit;
345
331
  });
346
332
  const drafts = [...mirrored.map(edit => instances.get(edit)), main];
347
- const jobs = [];
348
- const keys = new Map();
349
- const slots = drafts.map(draft => (draft.variableTransforms ?? []).map(job => {
350
- const key = JSON.stringify(job);
351
- let slot = keys.get(key);
352
- if (slot === undefined) {
353
- slot = jobs.length;
354
- keys.set(key, slot);
355
- jobs.push(job);
356
- }
357
- return slot;
358
- }));
333
+ const {
334
+ jobs,
335
+ slotOf
336
+ } = jobTable();
337
+ const slots = drafts.map(draft => (draft.variableTransforms ?? []).map(slotOf));
359
338
  if (jobs.length > MAX_TRANSFORM_JOBS || jobs.reduce((sum, job) => sum + job.value.length, 0) > MAX_TRANSFORM_WORK) {
360
339
  throw new CompletionLimitError();
361
340
  }
@@ -1,4 +1,5 @@
1
1
  import type { EditorLanguage } from './editor-language.ts';
2
+ export declare function followModKey(apple: boolean, onHeld: (held: boolean) => void): () => void;
2
3
  export declare class DefinitionLinkFeature {
3
4
  private readonly language;
4
5
  private held;
@@ -2,6 +2,27 @@ import { isIdentifierCharacter } from './completion-model.js';
2
2
  import { locationsOf } from './hover-content.js';
3
3
  import { lineTextOf } from './line-text.js';
4
4
  import { wordRangeAt } from './rename-model.js';
5
+ /* Reports the Mod key going down and up, from the window since the editor only says where the pointer is. */
6
+ export function followModKey(apple, onHeld) {
7
+ if (typeof window === 'undefined') {
8
+ return () => {};
9
+ }
10
+ const modKey = apple ? 'Meta' : 'Control';
11
+ const keys = event => {
12
+ if (event.key === modKey) {
13
+ onHeld(event.type === 'keydown');
14
+ }
15
+ };
16
+ const lost = () => onHeld(false);
17
+ window.addEventListener('keydown', keys);
18
+ window.addEventListener('keyup', keys);
19
+ window.addEventListener('blur', lost);
20
+ return () => {
21
+ window.removeEventListener('keydown', keys);
22
+ window.removeEventListener('keyup', keys);
23
+ window.removeEventListener('blur', lost);
24
+ };
25
+ }
5
26
  /*
6
27
  * The name under the pointer drawn as a link while Cmd (Ctrl elsewhere) is held and a server knows where it
7
28
  * is defined, which is what a Mod+click on it follows. The editor only says where the pointer is, so the
@@ -23,24 +44,7 @@ export class DefinitionLinkFeature {
23
44
  const offs = [editor.onHover(hover => {
24
45
  this.pointer = hover?.position ?? null;
25
46
  this.update();
26
- }), editor.onTextChange(() => this.clear()), editor.onViewChange(() => this.clear())];
27
- if (typeof window !== 'undefined') {
28
- const modKey = language.apple ? 'Meta' : 'Control';
29
- const keys = event => {
30
- if (event.key === modKey) {
31
- this.setModHeld(event.type === 'keydown');
32
- }
33
- };
34
- const lost = () => this.setModHeld(false);
35
- window.addEventListener('keydown', keys);
36
- window.addEventListener('keyup', keys);
37
- window.addEventListener('blur', lost);
38
- offs.push(() => {
39
- window.removeEventListener('keydown', keys);
40
- window.removeEventListener('keyup', keys);
41
- window.removeEventListener('blur', lost);
42
- });
43
- }
47
+ }), editor.onTextChange(() => this.clear()), editor.onViewChange(() => this.clear()), followModKey(language.apple, held => this.setModHeld(held))];
44
48
  language.onDispose(() => {
45
49
  for (const off of offs) {
46
50
  off();
@@ -9,6 +9,7 @@ export declare function severityOf(diagnostic: Diagnostic): EditorMarkerSeverity
9
9
  export declare function markerOf(diagnostic: Diagnostic): EditorMarker;
10
10
  export declare function problemCountsOf(diagnostics: readonly Diagnostic[]): ProblemCounts;
11
11
  export declare function comparePositions(left: EditorPosition, right: EditorPosition): number;
12
+ export declare function sameRange(left: EditorRange, right: EditorRange): boolean;
12
13
  export declare function endOfInsertion(start: EditorPosition, text: string): EditorPosition;
13
14
  export declare function shiftPosition(position: EditorPosition, change: EditorContentChange): EditorPosition;
14
15
  export declare function shiftRange(range: EditorRange, changes: readonly EditorContentChange[]): EditorRange;
@@ -22,7 +22,6 @@ export function markerOf(diagnostic) {
22
22
  message: diagnostic.message.length > TICK_MESSAGE_LENGTH ? `${diagnostic.message.slice(0, TICK_MESSAGE_LENGTH)}…` : diagnostic.message
23
23
  };
24
24
  }
25
- /* A diagnostic that names no severity counts as an error. */
26
25
  export function problemCountsOf(diagnostics) {
27
26
  const count = severity => diagnostics.filter(diagnostic => (diagnostic.severity ?? 1) === severity).length;
28
27
  return {
@@ -34,6 +33,9 @@ export function problemCountsOf(diagnostics) {
34
33
  export function comparePositions(left, right) {
35
34
  return left.line === right.line ? left.character - right.character : left.line - right.line;
36
35
  }
36
+ export function sameRange(left, right) {
37
+ return comparePositions(left.start, right.start) === 0 && comparePositions(left.end, right.end) === 0;
38
+ }
37
39
  /* Where an insertion of `text` at `start` ends, counting a line break of any kind once. */
38
40
  export function endOfInsertion(start, text) {
39
41
  const lines = text.split(/\r\n|\r|\n/);
@@ -1,7 +1,6 @@
1
1
  import { KEY_PRIORITY } from './key-priority.js';
2
- import { sameRange } from './hover-content.js';
3
2
  import { isShortcut } from './shortcut-keys.js';
4
- import { comparePositions, markerOf, neighborProblem, problemCountsOf, problemsAt, severityOf, shiftRange } from './diagnostics-model.js';
3
+ import { comparePositions, markerOf, neighborProblem, problemCountsOf, problemsAt, sameRange, severityOf, shiftRange } from './diagnostics-model.js';
5
4
  /*
6
5
  * The problems the servers report for one file. A report from a server replaces the one before it
7
6
  * from the same server, the editor draws them, and each one follows its text through edits made
@@ -13,4 +13,5 @@ export interface DiffSyntaxSources {
13
13
  readonly line: number;
14
14
  } | undefined;
15
15
  }
16
+ export declare function repaintOnTokens(ownerWindow: Window | null | undefined, tokens: DiffSourceTokens | undefined, refresh: () => void): () => void;
16
17
  export declare function diffSyntaxDocument(document: DiffDocument, language: string, theme: string): DiffSyntaxSources;
@@ -1,3 +1,34 @@
1
+ /* Calls `refresh` once per frame while tokens arrive, or in a microtask where the window draws no frames. */
2
+ export function repaintOnTokens(ownerWindow, tokens, refresh) {
3
+ let pending = false;
4
+ let stopped = false;
5
+ let frame;
6
+ const schedule = () => {
7
+ if (pending) {
8
+ return;
9
+ }
10
+ pending = true;
11
+ const paint = () => {
12
+ pending = false;
13
+ if (!stopped) {
14
+ refresh();
15
+ }
16
+ };
17
+ if (ownerWindow?.requestAnimationFrame !== undefined) {
18
+ frame = ownerWindow.requestAnimationFrame(paint);
19
+ } else {
20
+ queueMicrotask(paint);
21
+ }
22
+ };
23
+ const stop = tokens?.subscribe(schedule);
24
+ return () => {
25
+ stopped = true;
26
+ stop?.();
27
+ if (frame !== undefined) {
28
+ ownerWindow?.cancelAnimationFrame?.(frame);
29
+ }
30
+ };
31
+ }
1
32
  function canTokenize(source) {
2
33
  if (source.length > 20000) {
3
34
  return false;