@adecore/editor-react 0.23.1 → 0.24.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 (109) hide show
  1. package/dist/AnchoredPopup.d.ts +2 -1
  2. package/dist/AnchoredPopup.js +2 -2
  3. package/dist/CodeAuthorsCard.d.ts +4 -0
  4. package/dist/CodeAuthorsCard.js +8 -4
  5. package/dist/CompletionPopup.js +7 -3
  6. package/dist/EditorView.d.ts +8 -3
  7. package/dist/EditorView.js +79 -9
  8. package/dist/HoverCard.d.ts +3 -1
  9. package/dist/HoverCard.js +2 -2
  10. package/dist/LanguagePopups.d.ts +8 -4
  11. package/dist/LanguagePopups.js +4 -4
  12. package/dist/PickPopup.js +2 -1
  13. package/dist/RenameCard.js +10 -8
  14. package/dist/RenderedDocs.d.ts +10 -4
  15. package/dist/RenderedDocs.js +10 -6
  16. package/dist/call-hierarchy.js +15 -14
  17. package/dist/code-actions.js +5 -2
  18. package/dist/completion-model.js +3 -1
  19. package/dist/completion.d.ts +4 -2
  20. package/dist/completion.js +49 -27
  21. package/dist/diagnostics-model.d.ts +1 -0
  22. package/dist/diagnostics-model.js +1 -1
  23. package/dist/diagnostics.d.ts +1 -0
  24. package/dist/diagnostics.js +28 -10
  25. package/dist/diff-overlays.js +2 -2
  26. package/dist/editor-language.d.ts +3 -1
  27. package/dist/editor-language.js +10 -0
  28. package/dist/format-on-type.js +2 -3
  29. package/dist/highlights.js +3 -2
  30. package/dist/history.js +3 -2
  31. package/dist/holder.d.ts +3 -3
  32. package/dist/holder.js +2 -1
  33. package/dist/host-types.d.ts +3 -0
  34. package/dist/hover.d.ts +5 -0
  35. package/dist/hover.js +13 -5
  36. package/dist/index.d.ts +6 -2
  37. package/dist/index.js +3 -1
  38. package/dist/key-priority.d.ts +23 -0
  39. package/dist/key-priority.js +39 -0
  40. package/dist/locales/en.json +2 -1
  41. package/dist/locales/nl.json +2 -1
  42. package/dist/models.d.ts +2 -2
  43. package/dist/models.js +2 -2
  44. package/dist/navigation.js +3 -2
  45. package/dist/peek.js +3 -2
  46. package/dist/pick.js +2 -1
  47. package/dist/popup-focus.d.ts +5 -0
  48. package/dist/popup-focus.js +12 -0
  49. package/dist/popups.d.ts +1 -1
  50. package/dist/project-language.js +5 -4
  51. package/dist/project-problems.d.ts +10 -1
  52. package/dist/project-problems.js +34 -10
  53. package/dist/proposal-model.d.ts +0 -1
  54. package/dist/proposal-model.js +0 -8
  55. package/dist/rename-model.d.ts +2 -1
  56. package/dist/rename-model.js +57 -0
  57. package/dist/rename.d.ts +1 -0
  58. package/dist/rename.js +21 -5
  59. package/dist/rendered-docs.js +3 -2
  60. package/dist/signature.js +4 -3
  61. package/dist/snippet-session.d.ts +1 -1
  62. package/dist/snippet-session.js +8 -2
  63. package/dist/symbol-picker.js +3 -2
  64. package/package.json +6 -6
  65. package/src/AnchoredPopup.tsx +4 -2
  66. package/src/CodeAuthorsCard.tsx +21 -12
  67. package/src/CompletionPopup.tsx +11 -1
  68. package/src/EditorView.tsx +122 -11
  69. package/src/HoverCard.tsx +6 -1
  70. package/src/LanguagePopups.tsx +23 -4
  71. package/src/PickPopup.tsx +9 -1
  72. package/src/RenameCard.tsx +15 -9
  73. package/src/RenderedDocs.tsx +30 -8
  74. package/src/call-hierarchy.ts +15 -14
  75. package/src/code-actions.ts +5 -2
  76. package/src/completion-model.ts +3 -1
  77. package/src/completion.ts +48 -28
  78. package/src/diagnostics-model.ts +1 -1
  79. package/src/diagnostics.ts +29 -13
  80. package/src/diff-overlays.tsx +12 -8
  81. package/src/editor-language.ts +12 -1
  82. package/src/format-on-type.ts +2 -3
  83. package/src/highlights.ts +3 -2
  84. package/src/history.ts +3 -2
  85. package/src/holder.ts +5 -4
  86. package/src/host-types.ts +5 -0
  87. package/src/hover.ts +19 -5
  88. package/src/index.ts +6 -2
  89. package/src/key-priority.ts +49 -0
  90. package/src/locales/en.json +2 -1
  91. package/src/locales/nl.json +2 -1
  92. package/src/models.ts +1 -1
  93. package/src/navigation.ts +3 -2
  94. package/src/peek.ts +3 -2
  95. package/src/pick.ts +2 -1
  96. package/src/popup-focus.ts +17 -0
  97. package/src/popups.ts +1 -2
  98. package/src/project-language.ts +5 -4
  99. package/src/project-problems.ts +46 -11
  100. package/src/proposal-model.ts +0 -9
  101. package/src/rename-model.ts +60 -1
  102. package/src/rename.ts +21 -5
  103. package/src/rendered-docs.ts +3 -2
  104. package/src/signature.ts +4 -3
  105. package/src/snippet-session.ts +9 -3
  106. package/src/symbol-picker.ts +3 -2
  107. package/dist/relative-time.d.ts +0 -1
  108. package/dist/relative-time.js +0 -9
  109. package/src/relative-time.ts +0 -10
@@ -1,6 +1,7 @@
1
1
  import type { EditorPosition, EditorRange } from '@adecore/editor';
2
- import type { Location, PrepareRenameResult, TextEdit, WorkspaceEdit } from '@adecore/lsp';
2
+ import type { DocumentSymbol, DocumentSymbolResult, Location, PrepareRenameResult, TextEdit, WorkspaceEdit } from '@adecore/lsp';
3
3
  import { isIdentifierCharacter } from './completion-model.ts';
4
+ import { rangeHolds } from './diagnostics-model.ts';
4
5
  import { textEntriesOf } from './workspace-edit.ts';
5
6
 
6
7
  /* The identifier the caret is in or touches; null where there is none. */
@@ -59,6 +60,64 @@ export function occurrencesOf(locations: readonly Location[], uri: string): Occu
59
60
  };
60
61
  }
61
62
 
63
+ /* LSP's SymbolKind in words, indexed by its number. */
64
+ const SYMBOL_KINDS = [
65
+ '',
66
+ 'file',
67
+ 'module',
68
+ 'namespace',
69
+ 'package',
70
+ 'class',
71
+ 'method',
72
+ 'property',
73
+ 'field',
74
+ 'constructor',
75
+ 'enum',
76
+ 'interface',
77
+ 'function',
78
+ 'variable',
79
+ 'constant',
80
+ 'string',
81
+ 'number',
82
+ 'boolean',
83
+ 'array',
84
+ 'object',
85
+ 'key',
86
+ 'null',
87
+ 'enum member',
88
+ 'struct',
89
+ 'event',
90
+ 'operator',
91
+ 'type parameter'
92
+ ];
93
+
94
+ /*
95
+ * The kind of the symbol a rename names, in words, from the outline of the open file: the symbol whose name
96
+ * holds one of the places of the name there. Undefined when the outline does not list it, as with most locals.
97
+ */
98
+ export function renameKindOf(result: DocumentSymbolResult, name: string, places: readonly EditorRange[]): string | undefined {
99
+ if (result === null || result.length === 0) {
100
+ return undefined;
101
+ }
102
+ const named = (range: EditorRange) => places.some((place) => rangeHolds(range, place.start));
103
+ if ('range' in result[0]!) {
104
+ let found: DocumentSymbol | undefined;
105
+ const walk = (symbols: readonly DocumentSymbol[]): void => {
106
+ for (const symbol of symbols) {
107
+ if (named(symbol.selectionRange)) {
108
+ found = symbol;
109
+ }
110
+ walk(symbol.children ?? []);
111
+ }
112
+ };
113
+ walk(result as DocumentSymbol[]);
114
+ return found === undefined ? undefined : SYMBOL_KINDS[found.kind];
115
+ }
116
+ // A flat list only gives the whole span of a symbol, so the name has to match as well.
117
+ const found = result.find((symbol) => 'location' in symbol && symbol.name === name && named(symbol.location.range));
118
+ return found === undefined ? undefined : SYMBOL_KINDS[found.kind];
119
+ }
120
+
62
121
  /* One line a rename changes: the line as it is and as it will be. */
63
122
  export interface RenameRow {
64
123
  /* One-based. */
package/src/rename.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { StaleResultError, type Location, type PrepareRenameResult, type WorkspaceEdit } from '@adecore/lsp';
2
+ import { KEY_PRIORITY } from './key-priority.ts';
2
3
  import type { EditorRange } from '@adecore/editor';
3
4
  import type { EditorLanguage } from './editor-language.ts';
4
5
  import { lineTextOf } from './line-text.ts';
5
6
  import type { RenameView } from './popups.ts';
6
- import { occurrencesOf, renamePreviewOf, renameTargetOf, wordRangeAt, type RenameTarget } from './rename-model.ts';
7
+ import { occurrencesOf, renameKindOf, renamePreviewOf, renameTargetOf, wordRangeAt, type RenameTarget } from './rename-model.ts';
7
8
  import { isShortcut } from './shortcut-keys.ts';
8
9
  import { resourceOperationsOf } from './workspace-edit.ts';
9
10
  import { messageOf } from './error-message.ts';
@@ -37,13 +38,13 @@ export class RenameFeature {
37
38
  this.language = language;
38
39
  const { editor } = language;
39
40
  const offs = [
40
- editor.onKeyDown((event) => {
41
+ language.onKey((event) => {
41
42
  if (!isShortcut(this.language.shortcuts.rename, event)) {
42
43
  return false;
43
44
  }
44
45
  void this.start();
45
46
  return true;
46
- }),
47
+ }, KEY_PRIORITY.rename),
47
48
  editor.onTextChange(() => this.cancel())
48
49
  ];
49
50
  language.onDispose(() => {
@@ -218,8 +219,9 @@ export class RenameFeature {
218
219
  start: { line: Math.max(0, line - CONTEXT_LINES), character: 0 },
219
220
  end: { line: line + CONTEXT_LINES, character: Number.MAX_SAFE_INTEGER }
220
221
  });
221
- const uses = await this.linesOf(locations ?? []);
222
- const names = await suggestNames({ uri, languageId, name: session.original, context, uses }, session.names.signal);
222
+ const [uses, kind] = await Promise.all([this.linesOf(locations ?? []), this.kindOf(session, locations ?? [])]);
223
+ const request = { uri, languageId, name: session.original, ...(kind === undefined ? {} : { kind }), context, uses };
224
+ const names = await suggestNames(request, session.names.signal);
223
225
  if (this.session === session && names.length > 0) {
224
226
  this.publish({ suggestions: names });
225
227
  }
@@ -228,6 +230,20 @@ export class RenameFeature {
228
230
  }
229
231
  }
230
232
 
233
+ /* So a suggester knows a function from a variable; a rename on a use finds the kind through the declaration's place in this file. */
234
+ private async kindOf(session: Session, locations: readonly Location[]): Promise<string | undefined> {
235
+ const { project, uri } = this.language;
236
+ if (!project.service.supports('textDocument/documentSymbol', uri)) {
237
+ return undefined;
238
+ }
239
+ try {
240
+ const symbols = await project.service.documentSymbols(uri, { signal: session.names.signal, parallel: true });
241
+ return renameKindOf(symbols, session.original, [session.target.range, ...occurrencesOf(locations, uri).inFile]);
242
+ } catch {
243
+ return undefined;
244
+ }
245
+ }
246
+
231
247
  /* The text of the first lines that name the symbol, in the order the servers listed them, reading at most a few other files. */
232
248
  private async linesOf(locations: readonly Location[]): Promise<string[]> {
233
249
  const { project } = this.language;
@@ -1,4 +1,5 @@
1
1
  import { findDocComments, type DocComment, type EditorPosition, type EditorRange, type EditorReplacement, type EditorTrackedRange } from '@adecore/editor';
2
+ import { KEY_PRIORITY } from './key-priority.ts';
2
3
  import type { EditorLanguage } from './editor-language.ts';
3
4
  import { parseDocComment, type ParsedDocComment } from './doc-comment-model.ts';
4
5
  import { isShortcut } from './shortcut-keys.ts';
@@ -114,9 +115,9 @@ export class RenderedDocsFeature {
114
115
  this.toggleAt(line);
115
116
  }
116
117
  }),
117
- editor.onKeyDown((event) => {
118
+ language.onKey((event) => {
118
119
  return isShortcut(this.language.shortcuts.toggleRenderedDocs, event) && this.toggle();
119
- }),
120
+ }, KEY_PRIORITY.renderedDocs),
120
121
  editor.onViewChange(() => {
121
122
  this.sweep();
122
123
  this.releaseRevealed();
package/src/signature.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { SignatureHelp, SignatureHelpContext } from '@adecore/lsp';
2
+ import { KEY_PRIORITY } from './key-priority.ts';
2
3
  import type { EditorPosition, EditorTextChange } from '@adecore/editor';
3
4
  import type { EditorLanguage } from './editor-language.ts';
4
5
  import { signatureViewOf } from './signature-model.ts';
@@ -30,8 +31,8 @@ export class SignatureFeature {
30
31
  editor.onTextChange((change) => this.edited(change)),
31
32
  editor.onCaret(() => this.retrigger()),
32
33
  editor.onBlur(() => this.close()),
33
- editor.onKeyDown((event) => {
34
- if (event.key === 'Escape' && this.isOpen && this.language.popups.getState().callHierarchy == null) {
34
+ language.onKey((event) => {
35
+ if (event.key === 'Escape' && this.isOpen && this.language.popups.getState().callHierarchy === null) {
35
36
  this.close();
36
37
  return true;
37
38
  }
@@ -43,7 +44,7 @@ export class SignatureFeature {
43
44
  return this.cycle(event.key === 'ArrowDown' ? 1 : -1);
44
45
  }
45
46
  return false;
46
- })
47
+ }, KEY_PRIORITY.signature)
47
48
  ];
48
49
  language.onDispose(() => {
49
50
  for (const off of offs) {
@@ -3,6 +3,7 @@ import { SnippetFieldTransforms, type FieldTransformTarget } from './snippet-fie
3
3
  import { SnippetTransformError } from './snippet-transform-client.ts';
4
4
  import { acquireSnippetTransforms, type SnippetTransformLease } from './snippet-transform-queue.ts';
5
5
  import { realTimers, type Timers } from './timers.ts';
6
+ import { KEY_PRIORITY } from './key-priority.ts';
6
7
  import { MAX_TRANSFORM_CHARACTERS, type SnippetTransformJob } from './snippet-transform-protocol.ts';
7
8
  import type { EditorContentChange, EditorPosition, EditorRange, EditorTextChange } from '@adecore/editor';
8
9
  import { comparePositions, rangeHolds, shiftPosition } from './diagnostics-model.ts';
@@ -79,7 +80,7 @@ export interface SnippetInsertion {
79
80
  readonly fields?: readonly SnippetField[];
80
81
  }
81
82
 
82
- type SnippetHost = Pick<EditorLanguage, 'editor' | 'onDispose'> & Partial<Pick<EditorLanguage, 'popups' | 'pick' | 'project'>>;
83
+ type SnippetHost = Pick<EditorLanguage, 'editor' | 'onDispose'> & Partial<Pick<EditorLanguage, 'popups' | 'pick' | 'project' | 'onKey'>>;
83
84
  const MAX_FRAMES = 8;
84
85
  const MAX_RANGES = 1000;
85
86
 
@@ -185,7 +186,12 @@ export class SnippetFeature {
185
186
  this.transforms = language.project === undefined ? null : acquireSnippetTransforms(language.project, timers);
186
187
  const { editor } = language;
187
188
  this.fieldTransforms = new SnippetFieldTransforms(editor, this.transforms, (targets, values) => this.applyFieldTransforms(targets, values));
188
- const offs = [editor.onKeyDown((event) => this.key(event)), editor.onTextChange((change) => this.edited(change)), editor.onCaret(() => this.moved())];
189
+ const key = (event: KeyboardEvent): boolean => this.key(event);
190
+ const offs = [
191
+ language.onKey === undefined ? editor.onKeyDown(key) : language.onKey(key, KEY_PRIORITY.snippets),
192
+ editor.onTextChange((change) => this.edited(change)),
193
+ editor.onCaret(() => this.moved())
194
+ ];
189
195
  language.onDispose(() => {
190
196
  this.transforms?.dispose();
191
197
  this.end();
@@ -492,7 +498,7 @@ export class SnippetFeature {
492
498
  const popups = this.language.popups?.getState();
493
499
  if (
494
500
  popups !== undefined &&
495
- [popups.completion, popups.signature, popups.hover, popups.pick, popups.peek, popups.callHierarchy ?? null].some((popup) => popup !== null)
501
+ [popups.completion, popups.signature, popups.hover, popups.pick, popups.peek, popups.callHierarchy].some((popup) => popup !== null)
496
502
  ) {
497
503
  return false;
498
504
  }
@@ -1,4 +1,5 @@
1
1
  import { StaleResultError, type Location } from '@adecore/lsp';
2
+ import { KEY_PRIORITY } from './key-priority.ts';
2
3
  import { basenameOf } from './paths.ts';
3
4
  import type { EditorLanguage } from './editor-language.ts';
4
5
  import { entriesOf, workspaceEntriesOf, type SymbolEntry, type WorkspaceEntry } from './symbol-picker-model.ts';
@@ -17,13 +18,13 @@ export class SymbolPickerFeature {
17
18
 
18
19
  constructor(language: EditorLanguage) {
19
20
  this.language = language;
20
- const off = language.editor.onKeyDown((event) => {
21
+ const off = language.onKey((event) => {
21
22
  if (!isShortcut(this.language.shortcuts.goToSymbol, event)) {
22
23
  return false;
23
24
  }
24
25
  void this.open();
25
26
  return true;
26
- });
27
+ }, KEY_PRIORITY.symbolPicker);
27
28
  language.onDispose(() => {
28
29
  off();
29
30
  this.close();
@@ -1 +0,0 @@
1
- export declare function relativeTime(at: number, now: number): string;
@@ -1,9 +0,0 @@
1
- import { formatAgo, formatDay, formatDayWithYear } from '@adecore/ui/format';
2
- export function relativeTime(at, now) {
3
- const seconds = Math.max(0, now - at);
4
- if (seconds < 7 * 24 * 60 * 60) {
5
- return formatAgo(seconds * 1000);
6
- }
7
- const date = new Date(at * 1000);
8
- return date.getFullYear() === new Date(now * 1000).getFullYear() ? formatDay(date) : formatDayWithYear(date);
9
- }
@@ -1,10 +0,0 @@
1
- import { formatAgo, formatDay, formatDayWithYear } from '@adecore/ui/format';
2
-
3
- export function relativeTime(at: number, now: number): string {
4
- const seconds = Math.max(0, now - at);
5
- if (seconds < 7 * 24 * 60 * 60) {
6
- return formatAgo(seconds * 1000);
7
- }
8
- const date = new Date(at * 1000);
9
- return date.getFullYear() === new Date(now * 1000).getFullYear() ? formatDay(date) : formatDayWithYear(date);
10
- }