@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
@@ -12,10 +12,15 @@ export declare function shortcutsOf(keymap: Keymap, apple: boolean): {
12
12
  autoIndentLines: LanguageShortcut | null;
13
13
  callHierarchy: LanguageShortcut | null;
14
14
  codeActions: LanguageShortcut | null;
15
+ codeBlockEnd: LanguageShortcut | null;
16
+ codeBlockEndWithSelection: LanguageShortcut | null;
17
+ codeBlockStart: LanguageShortcut | null;
18
+ codeBlockStartWithSelection: LanguageShortcut | null;
15
19
  collapse: LanguageShortcut | null;
16
20
  collapseAll: LanguageShortcut | null;
17
21
  collapseDocComments: LanguageShortcut | null;
18
22
  collapseRecursively: LanguageShortcut | null;
23
+ completeCurrentStatement: LanguageShortcut | null;
19
24
  convertIndentsToSpaces: LanguageShortcut | null;
20
25
  convertIndentsToTabs: LanguageShortcut | null;
21
26
  decreaseFontSize: LanguageShortcut | null;
@@ -41,20 +46,32 @@ export declare function shortcutsOf(keymap: Keymap, apple: boolean): {
41
46
  goToImplementation: LanguageShortcut | null;
42
47
  goToSymbol: LanguageShortcut | null;
43
48
  goToTypeDefinition: LanguageShortcut | null;
49
+ hippieBackwardCompletion: LanguageShortcut | null;
50
+ hippieCompletion: LanguageShortcut | null;
44
51
  historyBack: LanguageShortcut | null;
45
52
  historyForward: LanguageShortcut | null;
46
53
  increaseFontSize: LanguageShortcut | null;
47
54
  inlineEdit: LanguageShortcut | null;
48
55
  joinLines: LanguageShortcut | null;
56
+ lastEditLocation: LanguageShortcut | null;
49
57
  matchBrace: LanguageShortcut | null;
58
+ methodDown: LanguageShortcut | null;
59
+ methodUp: LanguageShortcut | null;
50
60
  moveLineDown: LanguageShortcut | null;
51
61
  moveLineUp: LanguageShortcut | null;
62
+ moveStatementDown: LanguageShortcut | null;
63
+ moveStatementUp: LanguageShortcut | null;
64
+ nextChange: LanguageShortcut | null;
65
+ nextEditLocation: LanguageShortcut | null;
52
66
  nextHighlight: LanguageShortcut | null;
53
67
  nextProblem: LanguageShortcut | null;
54
68
  organizeImports: LanguageShortcut | null;
55
69
  parameterInfo: LanguageShortcut | null;
70
+ pasteAsPlainText: LanguageShortcut | null;
71
+ pasteFromHistory: LanguageShortcut | null;
56
72
  peekDefinition: LanguageShortcut | null;
57
73
  peekReferences: LanguageShortcut | null;
74
+ previousChange: LanguageShortcut | null;
58
75
  previousHighlight: LanguageShortcut | null;
59
76
  previousProblem: LanguageShortcut | null;
60
77
  quickInfo: LanguageShortcut | null;
@@ -92,10 +109,12 @@ export declare function shortcutsOf(keymap: Keymap, apple: boolean): {
92
109
  toggleLineComment: LanguageShortcut | null;
93
110
  toggleRenderedDocs: LanguageShortcut | null;
94
111
  triggerCompletion: LanguageShortcut | null;
112
+ typeHierarchy: LanguageShortcut | null;
95
113
  undo: LanguageShortcut | null;
96
114
  uniqueLines: LanguageShortcut | null;
97
115
  unselectOccurrence: LanguageShortcut | null;
98
116
  wordLeft: LanguageShortcut | null;
99
117
  wordRight: LanguageShortcut | null;
118
+ wrapWithAbbreviation: LanguageShortcut | null;
100
119
  };
101
120
  export declare function isShortcut(target: LanguageShortcut | null, event: KeyboardEvent): boolean;
package/dist/signature.js CHANGED
@@ -102,10 +102,9 @@ export class SignatureFeature {
102
102
  }
103
103
  options() {
104
104
  const {
105
- project,
106
105
  uri
107
106
  } = this.language;
108
- const options = project.service.providerOptions(METHOD, uri);
107
+ const options = this.language.service.providerOptions(METHOD, uri);
109
108
  return options === undefined ? null : {
110
109
  triggers: options.triggerCharacters ?? [],
111
110
  retriggers: options.retriggerCharacters ?? []
@@ -145,10 +144,9 @@ export class SignatureFeature {
145
144
  }
146
145
  ask(context) {
147
146
  const {
148
- project,
149
147
  uri
150
148
  } = this.language;
151
- if (!project.service.supports(METHOD, uri)) {
149
+ if (!this.language.service.supports(METHOD, uri)) {
152
150
  return;
153
151
  }
154
152
  this.timers.clear(this.timer);
@@ -164,7 +162,6 @@ export class SignatureFeature {
164
162
  async fetch(context, request) {
165
163
  const {
166
164
  editor,
167
- project,
168
165
  uri
169
166
  } = this.language;
170
167
  const caret = editor.getCaret();
@@ -173,7 +170,7 @@ export class SignatureFeature {
173
170
  this.controller = controller;
174
171
  let help;
175
172
  try {
176
- help = await project.service.signatureHelp(uri, caret, context, {
173
+ help = await this.language.service.signatureHelp(uri, caret, context, {
177
174
  signal: controller.signal
178
175
  });
179
176
  } catch {
@@ -1,6 +1,6 @@
1
1
  import { type Timers } from './timers.ts';
2
2
  import { type SnippetTransformJob } from './snippet-transform-protocol.ts';
3
- import type { EditorContentChange, EditorPosition, EditorRange } from '@adecore/editor';
3
+ import type { EditorApplyEditsOptions, EditorContentChange, EditorPosition, EditorRange } from '@adecore/editor';
4
4
  import type { EditorLanguage } from './editor-language.ts';
5
5
  import type { SnippetField, SnippetStop } from './snippet.ts';
6
6
  export declare function positionInInsertion(start: EditorPosition, text: string, offset: number): EditorPosition;
@@ -34,7 +34,7 @@ export declare class SnippetFeature {
34
34
  beginInstances(insertions: readonly SnippetInsertion[], options?: {
35
35
  nested?: boolean;
36
36
  }): void;
37
- applyEdits(changes: readonly EditorContentChange[], token: number, extras?: readonly EditorContentChange[]): boolean;
37
+ applyEdits(changes: readonly EditorContentChange[], token: number, extras?: readonly EditorContentChange[], options?: EditorApplyEditsOptions): boolean;
38
38
  end(): void;
39
39
  private closeChoices;
40
40
  private pop;
@@ -251,17 +251,17 @@ export class SnippetFeature {
251
251
  this.visit(0);
252
252
  }
253
253
  /* Imports can touch a hull's edge; their affinity must differ from edits inside its active field. */
254
- applyEdits(changes, token, extras = []) {
254
+ applyEdits(changes, token, extras = [], options) {
255
255
  const frame = this.frames.at(-1);
256
256
  const fits = frame !== undefined && changes.every(edit => extras.includes(edit) ? frame.instances.every(instance => comparePositions(edit.range.end, instance.hull.start) <= 0 || comparePositions(edit.range.start, instance.hull.end) >= 0) : frame.instances.some(instance => instance.selected.some(range => rangeHolds(range, edit.range.start) && rangeHolds(range, edit.range.end))));
257
257
  if (token !== this.generation || !fits) {
258
258
  this.end();
259
- return this.language.editor.applyEdits(changes);
259
+ return this.language.editor.applyEdits(changes, options);
260
260
  }
261
261
  this.applying = true;
262
262
  this.extras = extras;
263
263
  try {
264
- return this.language.editor.applyEdits(changes);
264
+ return this.language.editor.applyEdits(changes, options);
265
265
  } finally {
266
266
  this.applying = false;
267
267
  this.extras = [];
@@ -28,11 +28,10 @@ export class SymbolPickerFeature {
28
28
  }
29
29
  async open() {
30
30
  const {
31
- project,
32
31
  uri,
33
32
  popups
34
33
  } = this.language;
35
- if (!project.service.supports('textDocument/documentSymbol', uri)) {
34
+ if (!this.language.service.supports('textDocument/documentSymbol', uri)) {
36
35
  this.language.project.host.notify?.({
37
36
  id: TOAST_ID,
38
37
  kind: 'error',
@@ -41,7 +40,7 @@ export class SymbolPickerFeature {
41
40
  return;
42
41
  }
43
42
  try {
44
- const entries = entriesOf(await project.service.documentSymbols(uri));
43
+ const entries = entriesOf(await this.language.service.documentSymbols(uri));
45
44
  popups.setState({
46
45
  symbols: {
47
46
  file: basenameOf(this.language.uri),
@@ -87,13 +86,12 @@ export class SymbolPickerFeature {
87
86
  /* The symbols of the project that answer to a name, which is a request to the server and so waits for a pause in the typing. */
88
87
  async search(text, signal) {
89
88
  const {
90
- project,
91
89
  uri
92
90
  } = this.language;
93
- if (text === '' || !project.service.supports('workspace/symbol', uri)) {
91
+ if (text === '' || !this.language.service.supports('workspace/symbol', uri)) {
94
92
  return [];
95
93
  }
96
- return workspaceEntriesOf(await project.service.workspaceSymbols(uri, text, {
94
+ return workspaceEntriesOf(await this.language.service.workspaceSymbols(uri, text, {
97
95
  signal
98
96
  }));
99
97
  }
package/dist/symbols.js CHANGED
@@ -44,6 +44,23 @@ export function symbolSpansOf(result) {
44
44
  range: symbol.location.range
45
45
  }));
46
46
  }
47
+ function declarationsOf(result) {
48
+ if (result === null || result.length === 0) {
49
+ return [];
50
+ }
51
+ if (!('range' in result[0])) {
52
+ return result.map(symbol => symbol.location.range.start);
53
+ }
54
+ const stops = [];
55
+ const visit = symbols => {
56
+ for (const symbol of symbols) {
57
+ stops.push((symbol.selectionRange ?? symbol.range).start);
58
+ visit(symbol.children ?? []);
59
+ }
60
+ };
61
+ visit(result);
62
+ return stops;
63
+ }
47
64
  /* A line a declaration can have above it: blank, a comment, or an attribute. A bare `#` is no comment, since it heads a Markdown section. */
48
65
  const PRELUDE = /^\s*(?:$|\/\/|\/\*|\*|#\[)/;
49
66
  /*
@@ -112,18 +129,19 @@ export class SymbolsFeature {
112
129
  constructor(language, timers = realTimers) {
113
130
  const {
114
131
  editor,
115
- project,
116
132
  uri
117
133
  } = language;
118
134
  this.refresher = new Refresher(async signal => {
119
- if (!project.service.supports(METHOD, uri)) {
135
+ if (!language.service.supports(METHOD, uri)) {
120
136
  return;
121
137
  }
122
- const result = await project.service.documentSymbols(uri, {
138
+ const result = await language.service.documentSymbols(uri, {
123
139
  signal
124
140
  });
125
141
  if (!signal.aborted) {
126
142
  editor.setBlocks(result === null ? null : blocksOf(result, editor.getText()));
143
+ const declarations = declarationsOf(result);
144
+ editor.setDeclarations?.(declarations.length === 0 ? null : declarations);
127
145
  for (const listener of [...this.listeners]) {
128
146
  listener(result);
129
147
  }
@@ -138,6 +156,7 @@ export class SymbolsFeature {
138
156
  providers();
139
157
  this.refresher.dispose();
140
158
  editor.setBlocks(null);
159
+ editor.setDeclarations?.(null);
141
160
  });
142
161
  }
143
162
  /* Called with every answer that was not replaced before it came. */
package/dist/testing.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type LanguageService, type LanguageDocument, type LanguageRequestOptions, type DiagnosticsReport, type ContentChange, type Disposable, type ProviderOptions, type CallHierarchyIncomingCall, type CallHierarchyItem, type CallHierarchyOutgoingCall, type CodeAction, type CodeActionContext, type CodeLens, type Command, type CompletionContext, type CompletionItem, type CompletionResult, type DocumentHighlight, type DocumentSymbolResult, type FoldingRange, type FormattingOptions, type Hover, type InlayHint, type Location, type NavigationResult, type Position, type PrepareRenameResult, type Range, type SelectionRange, type SemanticTokens, type SemanticTokensDelta, type SignatureHelp, type SignatureHelpContext, type TextEdit, type WorkspaceEdit, type WorkspaceSymbolResult } from '@adecore/lsp';
1
+ import { type LanguageService, type LanguageDocument, type LanguageRequestOptions, type DiagnosticsReport, type ContentChange, type Disposable, type ProviderOptions, type CallHierarchyIncomingCall, type CallHierarchyItem, type CallHierarchyOutgoingCall, type CodeAction, type CodeActionContext, type CodeLens, type Command, type CompletionContext, type CompletionItem, type CompletionResult, type DocumentHighlight, type DocumentSymbolResult, type FoldingRange, type FormattingOptions, type Hover, type InlayHint, type Location, type NavigationResult, type Position, type PrepareRenameResult, type Range, type SelectionRange, type SemanticTokens, type SemanticTokensDelta, type SignatureHelp, type SignatureHelpContext, type TextEdit, type WorkspaceEdit, type WorkspaceSymbolResult, type Color, type ColorInformation, type ColorPresentation, type DocumentLink, type InlineCompletionContext, type InlineCompletionResult, type InlineValue, type InlineValueContext, type LinkedEditingRanges, type Moniker, type PreviousResultId, type ServerMessage, type ShowDocumentParams, type ShowDocumentResult, type TextDocumentSaveReason, type TypeHierarchyItem, type WorkspaceDiagnosticReport } from '@adecore/lsp';
2
2
  export { ManualTimers } from './timers.ts';
3
3
  export interface LanguageCall {
4
4
  method: string;
@@ -15,6 +15,8 @@ export declare class FakeLanguageService implements LanguageService {
15
15
  private readonly answers;
16
16
  private readonly diagnostics;
17
17
  private readonly changes;
18
+ private readonly messages;
19
+ private showDocument;
18
20
  respond(method: string, answer: (call: LanguageCall) => unknown, provider?: ProviderOptions): void;
19
21
  openDocument(document: LanguageDocument): Promise<void>;
20
22
  changeDocument(uri: string, changes: readonly ContentChange[]): Promise<void>;
@@ -58,4 +60,24 @@ export declare class FakeLanguageService implements LanguageService {
58
60
  outgoingCalls(uri: string, item: CallHierarchyItem, options?: LanguageRequestOptions): Promise<CallHierarchyOutgoingCall[] | null>;
59
61
  codeLenses(uri: string, options?: LanguageRequestOptions): Promise<CodeLens[] | null>;
60
62
  resolveCodeLens(uri: string, lens: CodeLens, options?: LanguageRequestOptions): Promise<CodeLens>;
63
+ documentLinks(uri: string, options?: LanguageRequestOptions): Promise<DocumentLink[] | null>;
64
+ resolveDocumentLink(uri: string, link: DocumentLink, options?: LanguageRequestOptions): Promise<DocumentLink>;
65
+ documentColors(uri: string, options?: LanguageRequestOptions): Promise<ColorInformation[] | null>;
66
+ colorPresentations(uri: string, color: Color, range: Range, options?: LanguageRequestOptions): Promise<ColorPresentation[] | null>;
67
+ linkedEditingRanges(uri: string, position: Position, options?: LanguageRequestOptions): Promise<LinkedEditingRanges | null>;
68
+ monikers(uri: string, position: Position, options?: LanguageRequestOptions): Promise<Moniker[] | null>;
69
+ prepareTypeHierarchy(uri: string, position: Position, options?: LanguageRequestOptions): Promise<TypeHierarchyItem[] | null>;
70
+ supertypes(uri: string, item: TypeHierarchyItem, options?: LanguageRequestOptions): Promise<TypeHierarchyItem[] | null>;
71
+ subtypes(uri: string, item: TypeHierarchyItem, options?: LanguageRequestOptions): Promise<TypeHierarchyItem[] | null>;
72
+ inlineValues(uri: string, range: Range, context: InlineValueContext, options?: LanguageRequestOptions): Promise<InlineValue[] | null>;
73
+ inlineCompletions(uri: string, position: Position, context: InlineCompletionContext, options?: LanguageRequestOptions): Promise<InlineCompletionResult>;
74
+ rangesFormatting(uri: string, ranges: readonly Range[], formatting: FormattingOptions, options?: LanguageRequestOptions): Promise<TextEdit[] | null>;
75
+ willSave(uri: string, reason?: TextDocumentSaveReason): Promise<void>;
76
+ willSaveWaitUntil(uri: string, reason?: TextDocumentSaveReason, options?: LanguageRequestOptions): Promise<TextEdit[] | null>;
77
+ workspaceDiagnostics(uri: string, previousResultIds?: readonly PreviousResultId[], options?: LanguageRequestOptions): Promise<WorkspaceDiagnosticReport>;
78
+ textDocumentContent(uri: string, options?: LanguageRequestOptions): Promise<string | null>;
79
+ onServerMessage(listener: (message: ServerMessage) => void): Disposable;
80
+ onShowDocument(handler: (params: ShowDocumentParams) => Promise<ShowDocumentResult>): Disposable;
81
+ message(message: ServerMessage): void;
82
+ show(params: ShowDocumentParams): Promise<boolean>;
61
83
  }
package/dist/testing.js CHANGED
@@ -7,6 +7,8 @@ export class FakeLanguageService {
7
7
  answers = new Map();
8
8
  diagnostics = new Set();
9
9
  changes = new Set();
10
+ messages = new Set();
11
+ showDocument = null;
10
12
  respond(method, answer, provider = {}) {
11
13
  this.answers.set(method, answer);
12
14
  this.providers.set(method, provider);
@@ -248,4 +250,116 @@ export class FakeLanguageService {
248
250
  resolveCodeLens(uri, lens, options = {}) {
249
251
  return this.request('codeLens/resolve', uri, lens, options);
250
252
  }
253
+ documentLinks(uri, options = {}) {
254
+ return this.request('textDocument/documentLink', uri, {}, options);
255
+ }
256
+ resolveDocumentLink(uri, link, options = {}) {
257
+ return this.request('documentLink/resolve', uri, link, options);
258
+ }
259
+ documentColors(uri, options = {}) {
260
+ return this.request('textDocument/documentColor', uri, {}, options);
261
+ }
262
+ colorPresentations(uri, color, range, options = {}) {
263
+ return this.request('textDocument/colorPresentation', uri, {
264
+ color,
265
+ range
266
+ }, options);
267
+ }
268
+ linkedEditingRanges(uri, position, options = {}) {
269
+ return this.request('textDocument/linkedEditingRange', uri, {
270
+ position
271
+ }, options);
272
+ }
273
+ monikers(uri, position, options = {}) {
274
+ return this.request('textDocument/moniker', uri, {
275
+ position
276
+ }, options);
277
+ }
278
+ prepareTypeHierarchy(uri, position, options = {}) {
279
+ return this.request('textDocument/prepareTypeHierarchy', uri, {
280
+ position
281
+ }, options);
282
+ }
283
+ supertypes(uri, item, options = {}) {
284
+ return this.request('typeHierarchy/supertypes', uri, {
285
+ item
286
+ }, options);
287
+ }
288
+ subtypes(uri, item, options = {}) {
289
+ return this.request('typeHierarchy/subtypes', uri, {
290
+ item
291
+ }, options);
292
+ }
293
+ inlineValues(uri, range, context, options = {}) {
294
+ return this.request('textDocument/inlineValue', uri, {
295
+ range,
296
+ context
297
+ }, options);
298
+ }
299
+ inlineCompletions(uri, position, context, options = {}) {
300
+ return this.request('textDocument/inlineCompletion', uri, {
301
+ position,
302
+ context
303
+ }, options);
304
+ }
305
+ rangesFormatting(uri, ranges, formatting, options = {}) {
306
+ return this.request('textDocument/rangesFormatting', uri, {
307
+ ranges,
308
+ options: formatting
309
+ }, options);
310
+ }
311
+ async willSave(uri, reason = 1) {
312
+ this.calls.push({
313
+ method: 'textDocument/willSave',
314
+ uri,
315
+ params: {
316
+ reason
317
+ },
318
+ options: {}
319
+ });
320
+ }
321
+ async willSaveWaitUntil(uri, reason = 1, options = {}) {
322
+ return this.supports('textDocument/willSaveWaitUntil', uri) ? this.request('textDocument/willSaveWaitUntil', uri, {
323
+ reason
324
+ }, options) : null;
325
+ }
326
+ workspaceDiagnostics(uri, previousResultIds = [], options = {}) {
327
+ return this.request('workspace/diagnostic', uri, {
328
+ previousResultIds
329
+ }, options);
330
+ }
331
+ async textDocumentContent(uri, options = {}) {
332
+ if (!this.supports('workspace/textDocumentContent', uri)) {
333
+ return null;
334
+ }
335
+ return (await this.request('workspace/textDocumentContent', uri, {
336
+ uri
337
+ }, options))?.text ?? null;
338
+ }
339
+ onServerMessage(listener) {
340
+ this.messages.add(listener);
341
+ return {
342
+ dispose: () => this.messages.delete(listener)
343
+ };
344
+ }
345
+ onShowDocument(handler) {
346
+ this.showDocument = handler;
347
+ return {
348
+ dispose: () => {
349
+ if (this.showDocument === handler) {
350
+ this.showDocument = null;
351
+ }
352
+ }
353
+ };
354
+ }
355
+ /* Sends a message as a server would. */
356
+ message(message) {
357
+ for (const listener of this.messages) {
358
+ listener(message);
359
+ }
360
+ }
361
+ /* Asks the host to show a document as a server would; false when nothing handles it. */
362
+ async show(params) {
363
+ return (await this.showDocument?.(params))?.success ?? false;
364
+ }
251
365
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adecore/editor-react",
3
- "version": "0.42.0",
3
+ "version": "0.43.0",
4
4
  "type": "module",
5
5
  "description": "Language-enabled editors, feature coordination and React popups over injected language services.",
6
6
  "license": "FSL-1.1-MIT",
@@ -62,9 +62,10 @@
62
62
  "test": "bun test --conditions=source"
63
63
  },
64
64
  "dependencies": {
65
- "@adecore/editor": "0.42.0",
66
- "@adecore/lsp": "0.42.0",
67
- "@adecore/merge": "0.42.0",
65
+ "@adecore/editor": "0.43.0",
66
+ "@adecore/lsp": "0.43.0",
67
+ "@adecore/merge": "0.43.0",
68
+ "emmet": "^2.4.11",
68
69
  "lucide-react": "^1.61.0",
69
70
  "react-markdown": "^10.1.0",
70
71
  "remark-breaks": "^4.0.0",
@@ -72,14 +73,14 @@
72
73
  "zustand": "^5.0.16"
73
74
  },
74
75
  "peerDependencies": {
75
- "@adecore/ui": "^0.42.0",
76
+ "@adecore/ui": "^0.43.0",
76
77
  "i18next": "^26.4.2",
77
78
  "react": "^19.3.0",
78
79
  "react-dom": "^19.3.0",
79
80
  "react-i18next": "^17.0.15"
80
81
  },
81
82
  "devDependencies": {
82
- "@adecore/ui": "0.42.0",
83
+ "@adecore/ui": "0.43.0",
83
84
  "@types/bun": "^1.4.4",
84
85
  "@types/react": "^19.3.0",
85
86
  "@types/react-dom": "^19.3.0",
@@ -9,7 +9,7 @@ import { CALL_HIERARCHY_HEIGHT } from './call-hierarchy.ts';
9
9
  import { PanelResizeHandle } from './PanelResizeHandle.tsx';
10
10
  import { usePanelBody } from './use-panel-body.ts';
11
11
  import { PANEL_BODY_HEIGHT } from './panel-size.ts';
12
- import { CALL_LEVEL_LIMIT, type CallDirection, type CallRow } from './call-hierarchy-model.ts';
12
+ import { CALL_LEVEL_LIMIT, isTypeDirection, type CallDirection, type CallRow } from './call-hierarchy-model.ts';
13
13
  import type { EditorLanguage } from './editor-language.ts';
14
14
  import { basenameOf, relativeTo } from './paths.ts';
15
15
  import { PathText } from './PathText.tsx';
@@ -125,7 +125,12 @@ function Tree({ language, view }: { language: EditorLanguage; view: CallHierarch
125
125
  useInputPopup(language, treeId, activeId);
126
126
 
127
127
  return (
128
- <div id={treeId} role="tree" aria-label={t('language.callHierarchy.label')} className="w-1/2 shrink-0 overflow-y-auto border-r border-border py-1">
128
+ <div
129
+ id={treeId}
130
+ role="tree"
131
+ aria-label={t(isTypeDirection(view.direction) ? 'language.callHierarchy.typesLabel' : 'language.callHierarchy.label')}
132
+ className="w-1/2 shrink-0 overflow-y-auto border-r border-border py-1"
133
+ >
129
134
  {view.rows.map((row) => {
130
135
  const active = row.id === view.active;
131
136
  const ref = active ? activeRow : undefined;
@@ -174,11 +179,14 @@ function StatusText({ row, direction }: { row: Extract<CallRow, { kind: 'status'
174
179
  );
175
180
  }
176
181
 
177
- return (
178
- <span className="font-sans text-xs text-text-muted">
179
- {t(direction === 'incoming' ? 'language.callHierarchy.noIncoming' : 'language.callHierarchy.noOutgoing')}
180
- </span>
181
- );
182
+ const empty = {
183
+ incoming: 'noIncoming',
184
+ outgoing: 'noOutgoing',
185
+ supertypes: 'noSupertypes',
186
+ subtypes: 'noSubtypes'
187
+ } as const;
188
+
189
+ return <span className="font-sans text-xs text-text-muted">{t(`language.callHierarchy.${empty[direction]}`)}</span>;
182
190
  }
183
191
 
184
192
  /*
@@ -210,10 +218,17 @@ export function CallHierarchyPanel({ language, view }: { language: EditorLanguag
210
218
  value={view.direction}
211
219
  onValueChange={(direction) => language.callHierarchy.setDirection(direction)}
212
220
  label={t('language.callHierarchy.direction')}
213
- options={[
214
- { id: 'incoming', label: t('language.callHierarchy.incoming') },
215
- { id: 'outgoing', label: t('language.callHierarchy.outgoing') }
216
- ]}
221
+ options={
222
+ isTypeDirection(view.direction)
223
+ ? [
224
+ { id: 'supertypes', label: t('language.callHierarchy.supertypes') },
225
+ { id: 'subtypes', label: t('language.callHierarchy.subtypes') }
226
+ ]
227
+ : [
228
+ { id: 'incoming', label: t('language.callHierarchy.incoming') },
229
+ { id: 'outgoing', label: t('language.callHierarchy.outgoing') }
230
+ ]
231
+ }
217
232
  />
218
233
  <span className="shrink-0 font-medium">{view.name}</span>
219
234
  <span className="shrink-0 font-medium text-text-muted">{path === null ? '' : basenameOf(path)}</span>
@@ -28,6 +28,7 @@ export function EditorContextMenu({ language, view, children }: { language: Edit
28
28
  implementation: () => void language.navigation.go('implementation'),
29
29
  peekReferences: () => void language.peek.open(),
30
30
  callHierarchy: () => void language.callHierarchy.open(),
31
+ typeHierarchy: () => void language.callHierarchy.open(undefined, 'supertypes'),
31
32
  renameSymbol: () => void language.rename.start(),
32
33
  codeActions: () => void language.codeActions.open()
33
34
  };
@@ -100,6 +101,12 @@ export function EditorContextMenu({ language, view, children }: { language: Edit
100
101
  {t('commands.formatDocument')}
101
102
  {language.shortcuts.formatDocument !== null && <Kbd shortcut={language.shortcuts.formatDocument} />}
102
103
  </ContextMenu.Item>
104
+ {language.codeActions.formatsSelection &&
105
+ language.editor
106
+ .getSelections()
107
+ .some((range) => range.start.line !== range.end.line || range.start.character !== range.end.character) && (
108
+ <ContextMenu.Item onClick={() => void language.codeActions.formatSelection()}>{t('commands.formatSelection')}</ContextMenu.Item>
109
+ )}
103
110
  </>
104
111
  )}
105
112
  </ContextMenu.Popup>
@@ -13,6 +13,7 @@ import { RenameCard } from './RenameCard.tsx';
13
13
  import { RenderedDocs } from './RenderedDocs.tsx';
14
14
  import { SymbolPicker } from './SymbolPicker.tsx';
15
15
  import { SignatureCard } from './SignatureCard.tsx';
16
+ import { WrapCard } from './WrapCard.tsx';
16
17
  import type { HoverView } from './popups.ts';
17
18
 
18
19
  export interface LanguagePopupsProps<Language extends EditorLanguage = EditorLanguage> {
@@ -44,6 +45,7 @@ export function LanguagePopups<Language extends EditorLanguage>({ language, cont
44
45
  const symbols = useStore(language.popups, (state) => state.symbols);
45
46
  const menu = useStore(language.popups, (state) => state.menu);
46
47
  const authors = useStore(language.popups, (state) => state.authors);
48
+ const wrap = useStore(language.popups, (state) => state.wrap);
47
49
 
48
50
  useEffect(() => language.editor.onViewChange(redraw), [language]);
49
51
 
@@ -53,6 +55,7 @@ export function LanguagePopups<Language extends EditorLanguage>({ language, cont
53
55
  const pickRect = pick === null ? null : language.editor.rectAt(pick.anchor);
54
56
  const renameRect = rename === null ? null : language.editor.rectAt(rename.range.start);
55
57
  const renameEnd = rename === null ? null : language.editor.rectAt(rename.range.end);
58
+ const wrapRect = wrap === null ? null : language.editor.rectAt(wrap.anchor);
56
59
 
57
60
  return (
58
61
  <>
@@ -85,6 +88,7 @@ export function LanguagePopups<Language extends EditorLanguage>({ language, cont
85
88
  {peek !== null && <PeekPanel language={language} view={peek} />}
86
89
  {callHierarchy !== null && <CallHierarchyPanel language={language} view={callHierarchy} />}
87
90
  {authors !== null && <CodeAuthorsCard language={language} view={authors} />}
91
+ {wrap !== null && wrapRect !== null && <WrapCard language={language} view={wrap} rect={wrapRect} />}
88
92
  {children}
89
93
  {rename !== null && renameRect !== null && <RenameCard language={language} view={rename} rect={renameRect} endRect={renameEnd} />}
90
94
  </>
package/src/PickPopup.tsx CHANGED
@@ -10,7 +10,34 @@ import type { PickPreview, PickView } from './popups.ts';
10
10
  const CARD =
11
11
  '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)';
12
12
 
13
+ /* The label with the part a query matched marked, whatever the case. */
14
+ function MatchedLabel({ label, query }: { label: string; query: string | null }) {
15
+ const start = query === null || query === '' ? -1 : label.toLowerCase().indexOf(query.toLowerCase());
16
+
17
+ if (query === null || start < 0) {
18
+ return label;
19
+ }
20
+
21
+ return (
22
+ <>
23
+ {label.slice(0, start)}
24
+ <mark className="bg-transparent font-semibold text-accent">{label.slice(start, start + query.length)}</mark>
25
+ {label.slice(start + query.length)}
26
+ </>
27
+ );
28
+ }
29
+
13
30
  function PreviewLines({ preview }: { preview: PickPreview }) {
31
+ if (preview.kind === 'text') {
32
+ return (
33
+ <div className="max-h-48 overflow-auto border-t border-border p-2 font-mono text-code whitespace-pre">
34
+ {preview.added.map((line, index) => (
35
+ <div key={index}>{line === '' ? ' ' : line}</div>
36
+ ))}
37
+ </div>
38
+ );
39
+ }
40
+
14
41
  return (
15
42
  <div className="flex flex-col gap-px border-t border-border p-1 font-mono text-code">
16
43
  {preview.removed.map((line, index) => (
@@ -89,7 +116,12 @@ export function PickPopup({ language, view, rect }: { language: EditorLanguage;
89
116
  onPointerDown={(event) => event.preventDefault()}
90
117
  {...keepFocusOutOfList(() => language.editor.focus())}
91
118
  >
92
- {view.title !== null && <div className="border-b border-border px-3 py-1.5 text-xs text-text-muted">{view.title}</div>}
119
+ {(view.title !== null || view.query !== null) && (
120
+ <div className="flex items-baseline gap-2 border-b border-border px-3 py-1.5 text-xs text-text-muted">
121
+ <span className="min-w-0 truncate">{view.title}</span>
122
+ {view.query !== null && view.query !== '' && <span className="ml-auto shrink-0 font-mono text-text">{view.query}</span>}
123
+ </div>
124
+ )}
93
125
  <div id={listId} role="listbox" className="min-h-0 flex-1 overflow-y-auto p-1">
94
126
  {view.groups.map((group, groupIndex) => (
95
127
  <div key={group.title ?? groupIndex} role="group" className="flex flex-col">
@@ -102,12 +134,15 @@ export function PickPopup({ language, view, rect }: { language: EditorLanguage;
102
134
  type="button"
103
135
  role="option"
104
136
  tabIndex={-1}
105
- aria-selected={row.id === view.active}
137
+ aria-selected={row.id === view.active || view.chosen.includes(row.id)}
106
138
  data-active={row.id === view.active}
107
- className="flex h-7 w-full items-center gap-2 rounded-md px-2 text-left text-xs cursor-row"
108
- onClick={() => language.pick.choose(row.id)}
139
+ data-chosen={view.chosen.includes(row.id)}
140
+ className="flex h-7 w-full items-center gap-2 rounded-md px-2 text-left text-xs cursor-row data-[chosen=true]:bg-accent/15"
141
+ onClick={(event) => language.pick.choose(row.id, { toggle: event.metaKey || event.ctrlKey, range: event.shiftKey })}
109
142
  >
110
- <span className={row.path ? 'shrink-0' : 'min-w-0 truncate'}>{row.label}</span>
143
+ <span className={row.path ? 'shrink-0' : 'min-w-0 truncate'}>
144
+ <MatchedLabel label={row.label} query={view.query} />
145
+ </span>
111
146
  {row.path ? (
112
147
  <PathText path={row.detail} className="ml-auto font-mono text-text-faint" />
113
148
  ) : (