@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.
- package/dist/CallHierarchyPanel.js +30 -55
- package/dist/CodeAuthorsCard.js +0 -1
- package/dist/CompletionPopup.js +150 -181
- package/dist/DiffView.d.ts +1 -1
- package/dist/DiffView.js +2 -29
- package/dist/PickPopup.js +54 -80
- package/dist/SplitDiffView.js +27 -57
- package/dist/SymbolPicker.js +2 -8
- package/dist/code-actions-model.js +2 -9
- package/dist/code-actions.js +10 -11
- package/dist/code-vision.js +2 -1
- package/dist/completion-model.js +2 -2
- package/dist/completion.js +13 -34
- package/dist/definition-link.d.ts +1 -0
- package/dist/definition-link.js +22 -18
- package/dist/diagnostics-model.d.ts +1 -0
- package/dist/diagnostics-model.js +3 -1
- package/dist/diagnostics.js +1 -2
- package/dist/diff-syntax.d.ts +1 -0
- package/dist/diff-syntax.js +31 -0
- package/dist/doc-comment-model.d.ts +1 -0
- package/dist/doc-comment-model.js +1 -1
- package/dist/folding.js +2 -3
- package/dist/format-on-type.js +6 -10
- package/dist/hover-content.d.ts +0 -2
- package/dist/hover-content.js +1 -9
- package/dist/inlay-hints.d.ts +0 -1
- package/dist/inlay-hints.js +11 -41
- package/dist/navigation.js +4 -14
- package/dist/peek.js +2 -12
- package/dist/popup-focus.d.ts +3 -1
- package/dist/popup-focus.js +33 -0
- package/dist/recent-choices.d.ts +0 -1
- package/dist/recent-choices.js +0 -4
- package/dist/rendered-docs.d.ts +1 -0
- package/dist/rendered-docs.js +15 -28
- package/dist/snippet-field-transforms.d.ts +5 -0
- package/dist/snippet-field-transforms.js +27 -16
- package/dist/snippet-session.js +8 -20
- package/dist/symbol-links.js +3 -10
- package/dist/symbol-picker-model.d.ts +1 -0
- package/dist/symbol-picker-model.js +7 -0
- package/dist/unique.d.ts +10 -0
- package/dist/unique.js +15 -0
- package/package.json +9 -9
- package/src/AnchoredPopup.tsx +2 -0
- package/src/BaselineStatus.tsx +1 -0
- package/src/CallHierarchyPanel.tsx +16 -6
- package/src/ChangeReview.tsx +7 -0
- package/src/CodeAuthorsCard.tsx +2 -5
- package/src/CompletionPopup.tsx +21 -15
- package/src/DiffView.tsx +53 -30
- package/src/EditorContextMenu.tsx +7 -0
- package/src/EditorView.tsx +28 -0
- package/src/FindReplace.tsx +8 -0
- package/src/HoverCard.tsx +8 -0
- package/src/HoverSections.tsx +11 -0
- package/src/LanguagePopups.tsx +1 -0
- package/src/PanelResizeHandle.tsx +8 -0
- package/src/PathText.tsx +1 -0
- package/src/PeekPanel.tsx +13 -0
- package/src/PickPopup.tsx +7 -6
- package/src/RenameCard.tsx +13 -0
- package/src/RenderedDocs.tsx +11 -0
- package/src/SplitDiffView.tsx +91 -56
- package/src/SymbolPicker.tsx +10 -18
- package/src/call-hierarchy-model.ts +10 -0
- package/src/call-hierarchy.ts +82 -0
- package/src/code-actions-model.ts +21 -9
- package/src/code-actions.ts +83 -7
- package/src/code-authors.ts +20 -0
- package/src/code-vision-model.ts +37 -0
- package/src/code-vision.ts +47 -3
- package/src/completion-call.ts +19 -0
- package/src/completion-model.ts +58 -2
- package/src/completion.ts +156 -30
- package/src/conflict-model.ts +4 -0
- package/src/context-menu-rows.ts +1 -0
- package/src/context-menu.ts +7 -0
- package/src/definition-link.ts +42 -18
- package/src/diagnostics-model.ts +19 -1
- package/src/diagnostics.ts +31 -2
- package/src/diff-expansion.ts +9 -0
- package/src/diff-overlays.tsx +8 -0
- package/src/diff-syntax.ts +54 -0
- package/src/diff-wordmarks.ts +8 -0
- package/src/doc-comment-model.ts +51 -1
- package/src/editor-language.ts +11 -0
- package/src/find-query.ts +7 -0
- package/src/folding.ts +10 -3
- package/src/format-on-type.ts +23 -10
- package/src/highlight-layers.ts +3 -0
- package/src/highlights.ts +18 -0
- package/src/history.ts +11 -0
- package/src/holder.ts +3 -0
- package/src/hover-content.ts +34 -17
- package/src/hover.ts +57 -0
- package/src/index.ts +4 -0
- package/src/inlay-hints.ts +86 -47
- package/src/key-priority.ts +2 -0
- package/src/line-edits.ts +8 -0
- package/src/models.ts +6 -0
- package/src/navigation-history.ts +5 -0
- package/src/navigation.ts +28 -13
- package/src/panel-size.ts +7 -0
- package/src/patch-decorations.ts +39 -0
- package/src/patch-layout.ts +47 -0
- package/src/peek-model.ts +21 -0
- package/src/peek.ts +49 -13
- package/src/pick.ts +35 -0
- package/src/popup-focus.ts +11 -1
- package/src/popup-placement.ts +5 -0
- package/src/popups.ts +2 -6
- package/src/project-language.ts +33 -0
- package/src/project-problems.ts +11 -0
- package/src/proposal-model.ts +40 -0
- package/src/recent-choices.ts +3 -5
- package/src/refresher.ts +3 -0
- package/src/rename-model.ts +28 -0
- package/src/rename.ts +56 -0
- package/src/rendered-docs.ts +89 -19
- package/src/review-group.ts +4 -0
- package/src/row-host.ts +12 -0
- package/src/selection-ranges.ts +6 -0
- package/src/semantic-delta.ts +7 -0
- package/src/semantic-model.ts +11 -0
- package/src/semantic-tokens.ts +37 -0
- package/src/shortcut-keys.ts +3 -0
- package/src/signature-model.ts +7 -0
- package/src/signature.ts +27 -0
- package/src/snippet-field-transforms.ts +48 -13
- package/src/snippet-indent.ts +10 -0
- package/src/snippet-range-order.ts +5 -0
- package/src/snippet-session.ts +173 -31
- package/src/snippet-transform-client.ts +14 -0
- package/src/snippet-transform-format.ts +98 -0
- package/src/snippet-transform-protocol.ts +5 -0
- package/src/snippet-transform-queue.ts +53 -0
- package/src/snippet-transform-worker.ts +14 -0
- package/src/snippet-variables.ts +9 -0
- package/src/snippet.ts +107 -0
- package/src/split-alignment.ts +10 -0
- package/src/split-scroll.ts +8 -0
- package/src/symbol-links.ts +9 -12
- package/src/symbol-picker-model.ts +25 -0
- package/src/symbol-picker.ts +9 -0
- package/src/symbols.ts +21 -0
- package/src/testing.ts +7 -0
- package/src/timers.ts +5 -0
- package/src/unique.ts +23 -0
- package/src/use-problem-counts.ts +4 -0
- package/src/view-state.ts +9 -0
- package/src/word-diff.ts +5 -0
- package/src/workspace-edit.ts +84 -0
package/src/SymbolPicker.tsx
CHANGED
|
@@ -8,30 +8,14 @@ import { formatNumber } from '@adecore/ui/format';
|
|
|
8
8
|
import type { EditorLanguage } from './editor-language.ts';
|
|
9
9
|
import { basenameOf } from './paths.ts';
|
|
10
10
|
import type { SymbolsView } from './popups.ts';
|
|
11
|
-
import {
|
|
12
|
-
filterEntries,
|
|
13
|
-
groupEntries,
|
|
14
|
-
letterOfKind,
|
|
15
|
-
modeOf,
|
|
16
|
-
toneOfKind,
|
|
17
|
-
type SymbolEntry,
|
|
18
|
-
type SymbolTone,
|
|
19
|
-
type WorkspaceEntry
|
|
20
|
-
} from './symbol-picker-model.ts';
|
|
21
|
-
|
|
22
|
-
const TONES: Record<SymbolTone, string> = {
|
|
23
|
-
callable: 'bg-accent-soft text-accent',
|
|
24
|
-
value: 'bg-status-running/15 text-status-running',
|
|
25
|
-
type: 'bg-status-idle/15 text-status-idle',
|
|
26
|
-
other: 'bg-surface-hover text-text-muted'
|
|
27
|
-
};
|
|
11
|
+
import { filterEntries, groupEntries, letterOfKind, modeOf, toneOfKind, type SymbolEntry, TONE_CLASSES, type WorkspaceEntry } from './symbol-picker-model.ts';
|
|
28
12
|
|
|
29
13
|
const SEARCH_DELAY_MS = 200;
|
|
30
14
|
|
|
31
15
|
/* The letter badge of a symbol kind, tinted by what the symbol is. */
|
|
32
16
|
export function SymbolBadge({ kind }: { kind: number }) {
|
|
33
17
|
return (
|
|
34
|
-
<span className={`grid size-4 shrink-0 place-items-center rounded-sm font-mono text-xs font-semibold ${
|
|
18
|
+
<span className={`grid size-4 shrink-0 place-items-center rounded-sm font-mono text-xs font-semibold ${TONE_CLASSES[toneOfKind(kind)]}`}>
|
|
35
19
|
{letterOfKind(kind)}
|
|
36
20
|
</span>
|
|
37
21
|
);
|
|
@@ -60,14 +44,17 @@ export function SymbolPicker({ language, view }: { language: EditorLanguage; vie
|
|
|
60
44
|
if (searchText === '') {
|
|
61
45
|
return;
|
|
62
46
|
}
|
|
47
|
+
|
|
63
48
|
const controller = new AbortController();
|
|
64
49
|
const text = searchText;
|
|
50
|
+
|
|
65
51
|
const timer = setTimeout(() => {
|
|
66
52
|
language.symbolPicker
|
|
67
53
|
.search(text, controller.signal)
|
|
68
54
|
.then((entries) => !controller.signal.aborted && setFound({ text, entries }))
|
|
69
55
|
.catch(() => undefined);
|
|
70
56
|
}, SEARCH_DELAY_MS);
|
|
57
|
+
|
|
71
58
|
return () => {
|
|
72
59
|
clearTimeout(timer);
|
|
73
60
|
controller.abort();
|
|
@@ -85,6 +72,7 @@ export function SymbolPicker({ language, view }: { language: EditorLanguage; vie
|
|
|
85
72
|
}
|
|
86
73
|
} else if (mode.mode === 'workspace') {
|
|
87
74
|
const place = places[index];
|
|
75
|
+
|
|
88
76
|
if (place !== undefined) {
|
|
89
77
|
language.symbolPicker.goToWorkspace(place);
|
|
90
78
|
}
|
|
@@ -109,6 +97,7 @@ export function SymbolPicker({ language, view }: { language: EditorLanguage; vie
|
|
|
109
97
|
}
|
|
110
98
|
|
|
111
99
|
let offset = 0;
|
|
100
|
+
|
|
112
101
|
return createPortal(
|
|
113
102
|
<div
|
|
114
103
|
className="fixed top-[12vh] left-1/2 z-(--z-popup) flex w-[560px] max-w-[calc(100vw-16px)] -translate-x-1/2 flex-col overflow-hidden rounded-lg border border-border bg-surface-raised text-text shadow-(--float-shadow)"
|
|
@@ -143,6 +132,7 @@ export function SymbolPicker({ language, view }: { language: EditorLanguage; vie
|
|
|
143
132
|
{mode.mode === 'workspace' &&
|
|
144
133
|
places.map((place, index) => {
|
|
145
134
|
const path = fileUriToPath(place.uri);
|
|
135
|
+
|
|
146
136
|
return (
|
|
147
137
|
<button
|
|
148
138
|
key={place.id}
|
|
@@ -167,11 +157,13 @@ export function SymbolPicker({ language, view }: { language: EditorLanguage; vie
|
|
|
167
157
|
groups.map((group) => {
|
|
168
158
|
const start = offset;
|
|
169
159
|
offset += group.entries.length;
|
|
160
|
+
|
|
170
161
|
return (
|
|
171
162
|
<div key={group.group} role="group">
|
|
172
163
|
<div className="px-2 pt-1.5 pb-0.5 text-xs font-medium text-text-faint">{t(`language.symbols.groups.${group.group}`)}</div>
|
|
173
164
|
{group.entries.map((entry, index) => {
|
|
174
165
|
const position = start + index;
|
|
166
|
+
|
|
175
167
|
return (
|
|
176
168
|
<button
|
|
177
169
|
key={entry.id}
|
|
@@ -87,6 +87,7 @@ export function rootsOf(items: readonly CallHierarchyItem[]): CallNode[] {
|
|
|
87
87
|
*/
|
|
88
88
|
export function childrenOf(direction: CallDirection, parent: CallNode, calls: readonly (CallHierarchyIncomingCall | CallHierarchyOutgoingCall)[]): CallNode[] {
|
|
89
89
|
const entries = calls.map((call) => ({ item: 'from' in call ? call.from : call.to, sites: call.fromRanges }));
|
|
90
|
+
|
|
90
91
|
return entries.map((entry, index) => ({
|
|
91
92
|
id: `${parent.id}.${index}`,
|
|
92
93
|
parent: parent.id,
|
|
@@ -105,8 +106,10 @@ export function childrenOf(direction: CallDirection, parent: CallNode, calls: re
|
|
|
105
106
|
/* The lines the tree draws: every root, and under an expanded node its status, its calls and the row that shows more. */
|
|
106
107
|
export function rowsOf(nodes: ReadonlyMap<string, CallNode>, roots: readonly string[]): CallRow[] {
|
|
107
108
|
const rows: CallRow[] = [];
|
|
109
|
+
|
|
108
110
|
const visit = (id: string, position: number, size: number): void => {
|
|
109
111
|
const node = nodes.get(id)!;
|
|
112
|
+
|
|
110
113
|
rows.push({
|
|
111
114
|
kind: 'node',
|
|
112
115
|
id,
|
|
@@ -119,9 +122,11 @@ export function rowsOf(nodes: ReadonlyMap<string, CallNode>, roots: readonly str
|
|
|
119
122
|
position,
|
|
120
123
|
size
|
|
121
124
|
});
|
|
125
|
+
|
|
122
126
|
if (!node.expanded) {
|
|
123
127
|
return;
|
|
124
128
|
}
|
|
129
|
+
|
|
125
130
|
if (node.load === 'loading') {
|
|
126
131
|
rows.push({ kind: 'status', id: statusIdOf(id), parent: id, level: node.level + 1, status: 'loading', message: null });
|
|
127
132
|
} else if (node.load === 'failed') {
|
|
@@ -129,13 +134,17 @@ export function rowsOf(nodes: ReadonlyMap<string, CallNode>, roots: readonly str
|
|
|
129
134
|
} else if (node.load === 'loaded' && node.children.length === 0) {
|
|
130
135
|
rows.push({ kind: 'status', id: statusIdOf(id), parent: id, level: node.level + 1, status: 'empty', message: null });
|
|
131
136
|
}
|
|
137
|
+
|
|
132
138
|
const shown = node.children.slice(0, node.shown);
|
|
133
139
|
shown.forEach((child, index) => visit(child, index + 1, node.children.length));
|
|
140
|
+
|
|
134
141
|
if (node.children.length > node.shown) {
|
|
135
142
|
rows.push({ kind: 'more', id: moreIdOf(id), level: node.level + 1, parent: id, hidden: node.children.length - node.shown });
|
|
136
143
|
}
|
|
137
144
|
};
|
|
145
|
+
|
|
138
146
|
roots.forEach((root, index) => visit(root, index + 1, roots.length));
|
|
147
|
+
|
|
139
148
|
return rows;
|
|
140
149
|
}
|
|
141
150
|
|
|
@@ -147,5 +156,6 @@ export function stepsOf(rows: readonly CallRow[]): CallRow[] {
|
|
|
147
156
|
/* The place a node goes to: its first call site, or the symbol itself for a root. */
|
|
148
157
|
export function targetOf(node: CallNode, site = 0): { uri: string; range: Range } {
|
|
149
158
|
const range = node.sites[site];
|
|
159
|
+
|
|
150
160
|
return range === undefined || node.siteUri === null ? { uri: node.item.uri, range: node.item.selectionRange } : { uri: node.siteUri, range };
|
|
151
161
|
}
|
package/src/call-hierarchy.ts
CHANGED
|
@@ -44,8 +44,10 @@ export class CallHierarchyFeature {
|
|
|
44
44
|
private prepared: CallHierarchyItem[] = [];
|
|
45
45
|
private direction: CallDirection = 'incoming';
|
|
46
46
|
private controller = new AbortController();
|
|
47
|
+
|
|
47
48
|
/* Raised whenever the tree starts over or closes, so an answer for the tree that was never lands in the one that is. */
|
|
48
49
|
private epoch = 0;
|
|
50
|
+
|
|
49
51
|
private previewEpoch = 0;
|
|
50
52
|
private texts = new Map<string, string>();
|
|
51
53
|
|
|
@@ -53,6 +55,7 @@ export class CallHierarchyFeature {
|
|
|
53
55
|
this.language = language;
|
|
54
56
|
this.timers = timers;
|
|
55
57
|
const { editor } = language;
|
|
58
|
+
|
|
56
59
|
const offs = [
|
|
57
60
|
language.onKey((event) => {
|
|
58
61
|
if (isShortcut(this.language.shortcuts.callHierarchy, event)) {
|
|
@@ -61,16 +64,20 @@ export class CallHierarchyFeature {
|
|
|
61
64
|
} else {
|
|
62
65
|
void this.open();
|
|
63
66
|
}
|
|
67
|
+
|
|
64
68
|
return true;
|
|
65
69
|
}
|
|
70
|
+
|
|
66
71
|
return this.key(event);
|
|
67
72
|
}, KEY_PRIORITY.callHierarchy),
|
|
68
73
|
editor.onTextChange(() => this.close())
|
|
69
74
|
];
|
|
75
|
+
|
|
70
76
|
language.onDispose(() => {
|
|
71
77
|
for (const off of offs) {
|
|
72
78
|
off();
|
|
73
79
|
}
|
|
80
|
+
|
|
74
81
|
this.close();
|
|
75
82
|
});
|
|
76
83
|
}
|
|
@@ -83,6 +90,7 @@ export class CallHierarchyFeature {
|
|
|
83
90
|
get supported(): boolean {
|
|
84
91
|
const { project, uri } = this.language;
|
|
85
92
|
const { service } = project;
|
|
93
|
+
|
|
86
94
|
return (
|
|
87
95
|
service.prepareCallHierarchy !== undefined &&
|
|
88
96
|
service.incomingCalls !== undefined &&
|
|
@@ -94,43 +102,57 @@ export class CallHierarchyFeature {
|
|
|
94
102
|
/* Opens the tree for the symbol at the position, on its callers unless told otherwise. */
|
|
95
103
|
async open(position: EditorPosition = this.language.editor.getCaret(), direction: CallDirection = 'incoming'): Promise<void> {
|
|
96
104
|
const { editor, project, uri } = this.language;
|
|
105
|
+
|
|
97
106
|
if (!this.supported) {
|
|
98
107
|
this.tell(this.say('unavailable'));
|
|
108
|
+
|
|
99
109
|
return;
|
|
100
110
|
}
|
|
111
|
+
|
|
101
112
|
this.close();
|
|
102
113
|
const epoch = this.epoch;
|
|
103
114
|
let items: CallHierarchyItem[] | null;
|
|
115
|
+
|
|
104
116
|
try {
|
|
105
117
|
items = await project.service.prepareCallHierarchy!(uri, position, { signal: this.controller.signal });
|
|
106
118
|
} catch (error) {
|
|
107
119
|
if (epoch === this.epoch && !(error instanceof StaleResultError)) {
|
|
108
120
|
this.tell(this.say('failed', { message: messageOf(error) }));
|
|
109
121
|
}
|
|
122
|
+
|
|
110
123
|
return;
|
|
111
124
|
}
|
|
125
|
+
|
|
112
126
|
if (epoch !== this.epoch) {
|
|
113
127
|
return;
|
|
114
128
|
}
|
|
129
|
+
|
|
115
130
|
if (items === null || items.length === 0) {
|
|
116
131
|
this.tell(this.say('none'));
|
|
132
|
+
|
|
117
133
|
return;
|
|
118
134
|
}
|
|
135
|
+
|
|
119
136
|
this.language.peek.close();
|
|
120
137
|
this.prepared = items;
|
|
121
138
|
this.direction = direction;
|
|
122
139
|
this.seed();
|
|
123
140
|
const active = this.roots[0]!;
|
|
141
|
+
|
|
124
142
|
this.language.popups.setState({
|
|
125
143
|
callHierarchy: { direction, container: null, name: items[0]!.name, rows: rowsOf(this.nodes, this.roots), active, site: 0, sites: 0, preview: null }
|
|
126
144
|
});
|
|
145
|
+
|
|
127
146
|
this.select(active);
|
|
147
|
+
|
|
128
148
|
// The editor draws a row once per id, so a tree opened over another needs an id of its own to get a container.
|
|
129
149
|
editor.setWidgets(
|
|
130
150
|
[{ id: `call-hierarchy-${epoch}`, line: position.line, height: CALL_HIERARCHY_HEIGHT, render: (container) => this.mounted(container) }],
|
|
131
151
|
OWNER
|
|
132
152
|
);
|
|
153
|
+
|
|
133
154
|
editor.focus();
|
|
155
|
+
|
|
134
156
|
for (const root of this.roots) {
|
|
135
157
|
void this.load(root);
|
|
136
158
|
}
|
|
@@ -138,9 +160,11 @@ export class CallHierarchyFeature {
|
|
|
138
160
|
|
|
139
161
|
close(): void {
|
|
140
162
|
this.reset();
|
|
163
|
+
|
|
141
164
|
if (this.language.popups.getState().callHierarchy === null) {
|
|
142
165
|
return;
|
|
143
166
|
}
|
|
167
|
+
|
|
144
168
|
this.language.popups.setState({ callHierarchy: null });
|
|
145
169
|
this.language.editor.setWidgets([], OWNER);
|
|
146
170
|
this.language.editor.setInputPopup(null);
|
|
@@ -150,17 +174,22 @@ export class CallHierarchyFeature {
|
|
|
150
174
|
/* Starts the tree over from the same symbols for the other direction. */
|
|
151
175
|
setDirection(direction: CallDirection): void {
|
|
152
176
|
const view = this.language.popups.getState().callHierarchy;
|
|
177
|
+
|
|
153
178
|
if (view === null || view.direction === direction) {
|
|
154
179
|
return;
|
|
155
180
|
}
|
|
181
|
+
|
|
156
182
|
this.reset();
|
|
157
183
|
this.direction = direction;
|
|
158
184
|
this.seed();
|
|
159
185
|
const active = this.roots[0]!;
|
|
186
|
+
|
|
160
187
|
this.language.popups.setState({
|
|
161
188
|
callHierarchy: { ...view, direction, rows: rowsOf(this.nodes, this.roots), active, site: 0, sites: 0, preview: null }
|
|
162
189
|
});
|
|
190
|
+
|
|
163
191
|
this.select(active);
|
|
192
|
+
|
|
164
193
|
for (const root of this.roots) {
|
|
165
194
|
void this.load(root);
|
|
166
195
|
}
|
|
@@ -169,18 +198,24 @@ export class CallHierarchyFeature {
|
|
|
169
198
|
/* Opens a node, asking the server for its calls the first time, or folds it away. */
|
|
170
199
|
toggle(id: string): void {
|
|
171
200
|
const node = this.nodes.get(id);
|
|
201
|
+
|
|
172
202
|
if (node === undefined) {
|
|
173
203
|
return;
|
|
174
204
|
}
|
|
205
|
+
|
|
175
206
|
if (node.expanded) {
|
|
176
207
|
this.patch(id, { expanded: false });
|
|
177
208
|
const view = this.language.popups.getState().callHierarchy;
|
|
209
|
+
|
|
178
210
|
if (view !== null && this.hasAncestor(view.active, id)) {
|
|
179
211
|
this.select(id);
|
|
180
212
|
}
|
|
213
|
+
|
|
181
214
|
return;
|
|
182
215
|
}
|
|
216
|
+
|
|
183
217
|
this.patch(id, { expanded: true });
|
|
218
|
+
|
|
184
219
|
if (node.load === 'idle' || node.load === 'failed') {
|
|
185
220
|
void this.load(id);
|
|
186
221
|
}
|
|
@@ -196,12 +231,15 @@ export class CallHierarchyFeature {
|
|
|
196
231
|
/* Brings the next stretch of calls of a level. */
|
|
197
232
|
showMore(parent: string): void {
|
|
198
233
|
const node = this.nodes.get(parent);
|
|
234
|
+
|
|
199
235
|
if (node === undefined) {
|
|
200
236
|
return;
|
|
201
237
|
}
|
|
238
|
+
|
|
202
239
|
this.patch(parent, { shown: Math.min(node.children.length, node.shown + CALL_LEVEL_LIMIT) });
|
|
203
240
|
const first = node.children[node.shown];
|
|
204
241
|
const view = this.language.popups.getState().callHierarchy;
|
|
242
|
+
|
|
205
243
|
// The row that offered more is gone once everything shows, so the selection moves on to what it brought.
|
|
206
244
|
if (first !== undefined && view?.active === moreIdOf(parent)) {
|
|
207
245
|
this.select(first);
|
|
@@ -212,12 +250,15 @@ export class CallHierarchyFeature {
|
|
|
212
250
|
select(id: string, site = 0): void {
|
|
213
251
|
const view = this.language.popups.getState().callHierarchy;
|
|
214
252
|
const node = this.nodes.get(id);
|
|
253
|
+
|
|
215
254
|
if (view === null || !view.rows.some((row) => row.id === id)) {
|
|
216
255
|
return;
|
|
217
256
|
}
|
|
257
|
+
|
|
218
258
|
const sites = node?.sites.length ?? view.sites;
|
|
219
259
|
const next = node === undefined ? view.site : Math.max(0, Math.min(Math.max(0, sites - 1), site));
|
|
220
260
|
this.language.popups.setState({ callHierarchy: { ...view, active: id, site: next, sites, preview: node === undefined ? view.preview : null } });
|
|
261
|
+
|
|
221
262
|
if (node !== undefined) {
|
|
222
263
|
void this.preview(node, next);
|
|
223
264
|
}
|
|
@@ -226,6 +267,7 @@ export class CallHierarchyFeature {
|
|
|
226
267
|
/* Steps through the places a call is made from when the active one has more than one. */
|
|
227
268
|
stepSite(direction: 1 | -1): void {
|
|
228
269
|
const view = this.language.popups.getState().callHierarchy;
|
|
270
|
+
|
|
229
271
|
if (view !== null && view.sites > 1) {
|
|
230
272
|
this.select(view.active, (view.site + direction + view.sites) % view.sites);
|
|
231
273
|
}
|
|
@@ -235,9 +277,11 @@ export class CallHierarchyFeature {
|
|
|
235
277
|
go(id: string): void {
|
|
236
278
|
const view = this.language.popups.getState().callHierarchy;
|
|
237
279
|
const node = this.nodes.get(id);
|
|
280
|
+
|
|
238
281
|
if (view === null || node === undefined) {
|
|
239
282
|
return;
|
|
240
283
|
}
|
|
284
|
+
|
|
241
285
|
const { uri, range } = targetOf(node, id === view.active ? view.site : 0);
|
|
242
286
|
this.close();
|
|
243
287
|
this.language.goTo({ uri, range });
|
|
@@ -261,6 +305,7 @@ export class CallHierarchyFeature {
|
|
|
261
305
|
|
|
262
306
|
private mounted(container: HTMLElement): void {
|
|
263
307
|
const view = this.language.popups.getState().callHierarchy;
|
|
308
|
+
|
|
264
309
|
if (view !== null && view.container !== container) {
|
|
265
310
|
this.language.popups.setState({ callHierarchy: { ...view, container } });
|
|
266
311
|
revealRow(this.timers, container);
|
|
@@ -270,43 +315,55 @@ export class CallHierarchyFeature {
|
|
|
270
315
|
private patch(id: string, change: Partial<CallNode>): void {
|
|
271
316
|
const node = this.nodes.get(id);
|
|
272
317
|
const view = this.language.popups.getState().callHierarchy;
|
|
318
|
+
|
|
273
319
|
if (node === undefined || view === null) {
|
|
274
320
|
return;
|
|
275
321
|
}
|
|
322
|
+
|
|
276
323
|
this.nodes.set(id, { ...node, ...change });
|
|
277
324
|
this.language.popups.setState({ callHierarchy: { ...view, rows: rowsOf(this.nodes, this.roots) } });
|
|
278
325
|
}
|
|
279
326
|
|
|
280
327
|
private async load(id: string): Promise<void> {
|
|
281
328
|
const node = this.nodes.get(id);
|
|
329
|
+
|
|
282
330
|
if (node === undefined || node.load === 'loading') {
|
|
283
331
|
return;
|
|
284
332
|
}
|
|
333
|
+
|
|
285
334
|
const { service, i18n } = this.language.project;
|
|
286
335
|
const { uri, editor } = this.language;
|
|
287
336
|
const { direction, epoch } = this;
|
|
288
337
|
const options = { signal: this.controller.signal, parallel: true };
|
|
289
338
|
this.patch(id, { load: 'loading', error: null });
|
|
339
|
+
|
|
290
340
|
try {
|
|
291
341
|
const calls =
|
|
292
342
|
direction === 'incoming' ? await service.incomingCalls!(uri, node.item, options) : await service.outgoingCalls!(uri, node.item, options);
|
|
343
|
+
|
|
293
344
|
if (epoch !== this.epoch) {
|
|
294
345
|
return;
|
|
295
346
|
}
|
|
347
|
+
|
|
296
348
|
const children = childrenOf(direction, node, calls ?? []);
|
|
349
|
+
|
|
297
350
|
for (const child of children) {
|
|
298
351
|
this.nodes.set(child.id, child);
|
|
299
352
|
}
|
|
353
|
+
|
|
300
354
|
this.patch(id, { load: 'loaded', children: children.map((child) => child.id), shown: Math.min(children.length, CALL_LEVEL_LIMIT) });
|
|
301
355
|
editor.announce(i18n.t(`editor:language.callHierarchy.announce.${direction}`, { count: children.length, name: node.item.name }));
|
|
302
356
|
} catch (error) {
|
|
303
357
|
if (epoch !== this.epoch) {
|
|
304
358
|
return;
|
|
305
359
|
}
|
|
360
|
+
|
|
306
361
|
if (error instanceof StaleResultError) {
|
|
307
362
|
this.close();
|
|
363
|
+
|
|
308
364
|
return;
|
|
309
365
|
}
|
|
366
|
+
|
|
310
367
|
this.patch(id, { load: 'failed', error: messageOf(error) });
|
|
311
368
|
}
|
|
312
369
|
}
|
|
@@ -316,6 +373,7 @@ export class CallHierarchyFeature {
|
|
|
316
373
|
const epoch = ++this.previewEpoch;
|
|
317
374
|
const { uri, range } = targetOf(node, site);
|
|
318
375
|
let text = this.texts.get(uri);
|
|
376
|
+
|
|
319
377
|
if (text === undefined) {
|
|
320
378
|
try {
|
|
321
379
|
text = (await project.readText(uri)) ?? undefined;
|
|
@@ -323,17 +381,22 @@ export class CallHierarchyFeature {
|
|
|
323
381
|
// A file that cannot be read shows no code, as one that is no text file does.
|
|
324
382
|
text = undefined;
|
|
325
383
|
}
|
|
384
|
+
|
|
326
385
|
if (epoch !== this.previewEpoch) {
|
|
327
386
|
return;
|
|
328
387
|
}
|
|
388
|
+
|
|
329
389
|
if (text !== undefined) {
|
|
330
390
|
this.texts.set(uri, text);
|
|
331
391
|
}
|
|
332
392
|
}
|
|
393
|
+
|
|
333
394
|
const view = this.language.popups.getState().callHierarchy;
|
|
395
|
+
|
|
334
396
|
if (view === null || view.active !== node.id) {
|
|
335
397
|
return;
|
|
336
398
|
}
|
|
399
|
+
|
|
337
400
|
const snippet = text === undefined ? null : snippetOf(text, range.start.line, range);
|
|
338
401
|
this.language.popups.setState({ callHierarchy: { ...view, preview: snippet === null ? null : { ...snippet, uri } } });
|
|
339
402
|
}
|
|
@@ -343,43 +406,55 @@ export class CallHierarchyFeature {
|
|
|
343
406
|
const owner = [moreIdOf(''), statusIdOf('')]
|
|
344
407
|
.map((suffix) => (id.endsWith(suffix) ? id.slice(0, -suffix.length) : null))
|
|
345
408
|
.find((found) => found !== null);
|
|
409
|
+
|
|
346
410
|
if (owner !== undefined && owner !== null) {
|
|
347
411
|
return owner === ancestor || this.hasAncestor(owner, ancestor);
|
|
348
412
|
}
|
|
413
|
+
|
|
349
414
|
for (let node = this.nodes.get(id); node !== undefined && node.parent !== null; node = this.nodes.get(node.parent)) {
|
|
350
415
|
if (node.parent === ancestor) {
|
|
351
416
|
return true;
|
|
352
417
|
}
|
|
353
418
|
}
|
|
419
|
+
|
|
354
420
|
return false;
|
|
355
421
|
}
|
|
356
422
|
|
|
357
423
|
private key(event: KeyboardEvent): boolean {
|
|
358
424
|
const view = this.language.popups.getState().callHierarchy;
|
|
425
|
+
|
|
359
426
|
if (view === null || event.metaKey || event.ctrlKey || event.altKey || event.shiftKey || event.isComposing) {
|
|
360
427
|
return false;
|
|
361
428
|
}
|
|
429
|
+
|
|
362
430
|
const steps = stepsOf(view.rows);
|
|
363
431
|
const index = steps.findIndex((row) => row.id === view.active);
|
|
364
432
|
const row: CallRow | undefined = steps[index];
|
|
433
|
+
|
|
365
434
|
switch (event.key) {
|
|
366
435
|
case 'ArrowDown':
|
|
367
436
|
this.land(steps[Math.min(steps.length - 1, index + 1)]);
|
|
437
|
+
|
|
368
438
|
return true;
|
|
369
439
|
case 'ArrowUp':
|
|
370
440
|
this.land(steps[Math.max(0, index - 1)]);
|
|
441
|
+
|
|
371
442
|
return true;
|
|
372
443
|
case 'Home':
|
|
373
444
|
this.land(steps[0]);
|
|
445
|
+
|
|
374
446
|
return true;
|
|
375
447
|
case 'End':
|
|
376
448
|
this.land(steps.at(-1));
|
|
449
|
+
|
|
377
450
|
return true;
|
|
378
451
|
case 'ArrowRight':
|
|
379
452
|
this.expandOrEnter(row, steps[index + 1]);
|
|
453
|
+
|
|
380
454
|
return true;
|
|
381
455
|
case 'ArrowLeft':
|
|
382
456
|
this.collapseOrLeave(row);
|
|
457
|
+
|
|
383
458
|
return true;
|
|
384
459
|
case 'Enter':
|
|
385
460
|
if (row?.kind === 'more') {
|
|
@@ -387,9 +462,11 @@ export class CallHierarchyFeature {
|
|
|
387
462
|
} else if (row !== undefined) {
|
|
388
463
|
this.go(row.id);
|
|
389
464
|
}
|
|
465
|
+
|
|
390
466
|
return true;
|
|
391
467
|
case 'Escape':
|
|
392
468
|
this.close();
|
|
469
|
+
|
|
393
470
|
return true;
|
|
394
471
|
default:
|
|
395
472
|
return false;
|
|
@@ -407,6 +484,7 @@ export class CallHierarchyFeature {
|
|
|
407
484
|
if (row?.kind !== 'node') {
|
|
408
485
|
return;
|
|
409
486
|
}
|
|
487
|
+
|
|
410
488
|
if (!row.expanded) {
|
|
411
489
|
this.toggle(row.id);
|
|
412
490
|
} else if (row.load === 'failed') {
|
|
@@ -421,11 +499,15 @@ export class CallHierarchyFeature {
|
|
|
421
499
|
if (row === undefined) {
|
|
422
500
|
return;
|
|
423
501
|
}
|
|
502
|
+
|
|
424
503
|
if (row.kind === 'node' && row.expanded && !row.leaf) {
|
|
425
504
|
this.toggle(row.id);
|
|
505
|
+
|
|
426
506
|
return;
|
|
427
507
|
}
|
|
508
|
+
|
|
428
509
|
const parent = row.kind === 'node' ? (this.nodes.get(row.id)?.parent ?? null) : row.parent;
|
|
510
|
+
|
|
429
511
|
if (parent !== null) {
|
|
430
512
|
this.select(parent);
|
|
431
513
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { applyTextEdits, type CodeAction, type Command, type Diagnostic, type Range, type WorkspaceEdit } from '@adecore/lsp';
|
|
2
2
|
import { comparePositions } from './diagnostics-model.ts';
|
|
3
|
+
import { uniqueBy } from './unique.ts';
|
|
3
4
|
import { resourceOperationsOf, textEntriesOf } from './workspace-edit.ts';
|
|
4
5
|
|
|
5
6
|
export type ActionGroup = 'quickfix' | 'rewrite' | 'extract' | 'inline' | 'move' | 'refactor' | 'source' | 'other';
|
|
@@ -21,6 +22,7 @@ export interface ActionEntry {
|
|
|
21
22
|
function inGroupOrder(entries: readonly ActionEntry[], order: readonly ActionGroup[]): ActionEntry[] {
|
|
22
23
|
return order.flatMap((group) => {
|
|
23
24
|
const own = entries.filter((entry) => entry.group === group);
|
|
25
|
+
|
|
24
26
|
return [...own.filter((entry) => entry.action.isPreferred === true), ...own.filter((entry) => entry.action.isPreferred !== true)];
|
|
25
27
|
});
|
|
26
28
|
}
|
|
@@ -30,17 +32,21 @@ export function groupOf(kind: string | undefined): ActionGroup {
|
|
|
30
32
|
if (kind === 'quickfix' || kind?.startsWith('quickfix.')) {
|
|
31
33
|
return 'quickfix';
|
|
32
34
|
}
|
|
35
|
+
|
|
33
36
|
for (const group of ['extract', 'inline', 'move', 'rewrite'] as const) {
|
|
34
37
|
if (kind === `refactor.${group}` || kind?.startsWith(`refactor.${group}.`)) {
|
|
35
38
|
return group;
|
|
36
39
|
}
|
|
37
40
|
}
|
|
41
|
+
|
|
38
42
|
if (kind === 'refactor' || kind?.startsWith('refactor.')) {
|
|
39
43
|
return 'refactor';
|
|
40
44
|
}
|
|
45
|
+
|
|
41
46
|
if (kind === 'source' || kind?.startsWith('source.')) {
|
|
42
47
|
return 'source';
|
|
43
48
|
}
|
|
49
|
+
|
|
44
50
|
return 'other';
|
|
45
51
|
}
|
|
46
52
|
|
|
@@ -53,20 +59,14 @@ export function actionsOf(result: readonly (CodeAction | Command)[] | null, orde
|
|
|
53
59
|
.map((item) => (typeof item.command === 'string' ? ({ title: item.title, command: item as Command } satisfies CodeAction) : (item as CodeAction)))
|
|
54
60
|
.filter((action) => action.disabled === undefined)
|
|
55
61
|
.map((action, index) => ({ id: String(index), action, group: groupOf(action.kind) }));
|
|
62
|
+
|
|
56
63
|
return inGroupOrder(entries, order);
|
|
57
64
|
}
|
|
58
65
|
|
|
59
66
|
/* 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. */
|
|
60
67
|
export function mergeEntries(lists: readonly (readonly ActionEntry[])[]): ActionEntry[] {
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
const key = `${entry.action.kind ?? ''}\0${entry.action.title}`;
|
|
64
|
-
if (seen.has(key)) {
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
seen.add(key);
|
|
68
|
-
return true;
|
|
69
|
-
});
|
|
68
|
+
const unique = uniqueBy(lists.flat(), (entry) => `${entry.action.kind ?? ''}\0${entry.action.title}`);
|
|
69
|
+
|
|
70
70
|
return inGroupOrder(unique, ACTION_GROUPS).map((entry, index) => ({ ...entry, id: String(index) }));
|
|
71
71
|
}
|
|
72
72
|
|
|
@@ -117,7 +117,9 @@ function linesOf(text: string): string[] {
|
|
|
117
117
|
export function previewOf(text: string, edit: WorkspaceEdit, uri: string): EditPreview | null {
|
|
118
118
|
const entries = textEntriesOf(edit);
|
|
119
119
|
const operations = resourceOperationsOf(edit);
|
|
120
|
+
|
|
120
121
|
const count = (kind: string): number => operations.filter((operation) => operation.kind === kind).length;
|
|
122
|
+
|
|
121
123
|
const moves = count('rename');
|
|
122
124
|
const creates = count('create');
|
|
123
125
|
const deletes = count('delete');
|
|
@@ -126,29 +128,39 @@ export function previewOf(text: string, edit: WorkspaceEdit, uri: string): EditP
|
|
|
126
128
|
const otherFiles = [...entries.keys()].filter((key) => key !== uri && !made.has(key)).length;
|
|
127
129
|
let removed: string[] = [];
|
|
128
130
|
let added: string[] = [];
|
|
131
|
+
|
|
129
132
|
if (own !== undefined) {
|
|
130
133
|
let after: string;
|
|
134
|
+
|
|
131
135
|
try {
|
|
132
136
|
after = applyTextEdits(text, own.flat());
|
|
133
137
|
} catch {
|
|
134
138
|
return null;
|
|
135
139
|
}
|
|
140
|
+
|
|
136
141
|
const before = linesOf(text);
|
|
137
142
|
const next = linesOf(after);
|
|
138
143
|
let head = 0;
|
|
144
|
+
|
|
139
145
|
while (head < before.length && head < next.length && before[head] === next[head]) {
|
|
140
146
|
head++;
|
|
141
147
|
}
|
|
148
|
+
|
|
142
149
|
let tail = 0;
|
|
150
|
+
|
|
143
151
|
while (tail < before.length - head && tail < next.length - head && before[before.length - 1 - tail] === next[next.length - 1 - tail]) {
|
|
144
152
|
tail++;
|
|
145
153
|
}
|
|
154
|
+
|
|
146
155
|
removed = before.slice(head, before.length - tail);
|
|
147
156
|
added = next.slice(head, next.length - tail);
|
|
148
157
|
}
|
|
158
|
+
|
|
149
159
|
if (removed.length === 0 && added.length === 0 && otherFiles === 0 && moves === 0 && creates === 0 && deletes === 0) {
|
|
150
160
|
return null;
|
|
151
161
|
}
|
|
162
|
+
|
|
152
163
|
const hiddenLines = Math.max(0, removed.length - PREVIEW_LINES) + Math.max(0, added.length - PREVIEW_LINES);
|
|
164
|
+
|
|
153
165
|
return { removed: removed.slice(0, PREVIEW_LINES), added: added.slice(0, PREVIEW_LINES), hiddenLines, otherFiles, moves, creates, deletes };
|
|
154
166
|
}
|