@adecore/editor-react 0.38.0 → 0.40.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.
- 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/dist/symbol-links.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { uniqueBy } from './unique.js';
|
|
1
2
|
/* The classes a name in a signature has: a pointer and an underline on hover, and the accent only then, so the signature stays calm. */
|
|
2
3
|
export const SYMBOL_LINK_CLASS = 'cursor-pointer hover:text-accent hover:underline';
|
|
3
4
|
const TYPE_NAME = /^[A-Z][A-Za-z0-9_]*$/;
|
|
@@ -34,8 +35,7 @@ export function placesOfName(result, name, fileText) {
|
|
|
34
35
|
candidates = named;
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
|
-
|
|
38
|
-
return candidates.map(symbol => 'range' in symbol.location ? symbol.location : {
|
|
38
|
+
return uniqueBy(candidates.map(symbol => 'range' in symbol.location ? symbol.location : {
|
|
39
39
|
uri: symbol.location.uri,
|
|
40
40
|
range: {
|
|
41
41
|
start: {
|
|
@@ -47,12 +47,5 @@ export function placesOfName(result, name, fileText) {
|
|
|
47
47
|
character: 0
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
-
})
|
|
51
|
-
const key = `${location.uri}\0${location.range.start.line}`;
|
|
52
|
-
if (seen.has(key)) {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
seen.add(key);
|
|
56
|
-
return true;
|
|
57
|
-
});
|
|
50
|
+
}), location => `${location.uri}\0${location.range.start.line}`);
|
|
58
51
|
}
|
|
@@ -4,6 +4,7 @@ export declare const SYMBOL_GROUPS: readonly SymbolGroup[];
|
|
|
4
4
|
export declare function groupOfKind(kind: number): SymbolGroup;
|
|
5
5
|
export declare function letterOfKind(kind: number): string;
|
|
6
6
|
export type SymbolTone = 'callable' | 'value' | 'type' | 'other';
|
|
7
|
+
export declare const TONE_CLASSES: Record<SymbolTone, string>;
|
|
7
8
|
export declare function toneOfKind(kind: number): SymbolTone;
|
|
8
9
|
export interface SymbolEntry {
|
|
9
10
|
readonly id: string;
|
|
@@ -38,6 +38,13 @@ const LETTERS = {
|
|
|
38
38
|
export function letterOfKind(kind) {
|
|
39
39
|
return kind === ENUM_MEMBER ? 'e' : LETTERS[groupOfKind(kind)];
|
|
40
40
|
}
|
|
41
|
+
/* The badge colors of a tone, shared by the symbol picker and the suggestions list. */
|
|
42
|
+
export const TONE_CLASSES = {
|
|
43
|
+
callable: 'bg-accent-soft text-accent',
|
|
44
|
+
value: 'bg-status-running/15 text-status-running',
|
|
45
|
+
type: 'bg-status-idle/15 text-status-idle',
|
|
46
|
+
other: 'bg-surface-hover text-text-muted'
|
|
47
|
+
};
|
|
41
48
|
const TONES = {
|
|
42
49
|
namespaces: 'other',
|
|
43
50
|
classes: 'type',
|
package/dist/unique.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function uniqueBy<T>(items: readonly T[], keyOf: (item: T) => string): T[];
|
|
2
|
+
export declare function placeKey(location: {
|
|
3
|
+
readonly uri: string;
|
|
4
|
+
readonly range: {
|
|
5
|
+
readonly start: {
|
|
6
|
+
readonly line: number;
|
|
7
|
+
readonly character: number;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
}): string;
|
package/dist/unique.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/* The items in order, without any whose key an earlier one had. */
|
|
2
|
+
export function uniqueBy(items, keyOf) {
|
|
3
|
+
const seen = new Set();
|
|
4
|
+
return items.filter(item => {
|
|
5
|
+
const key = keyOf(item);
|
|
6
|
+
if (seen.has(key)) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
seen.add(key);
|
|
10
|
+
return true;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export function placeKey(location) {
|
|
14
|
+
return `${location.uri}\0${location.range.start.line}\0${location.range.start.character}`;
|
|
15
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adecore/editor-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.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,25 +62,25 @@
|
|
|
62
62
|
"test": "bun test --conditions=source"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@adecore/editor": "0.
|
|
66
|
-
"@adecore/lsp": "0.
|
|
67
|
-
"@adecore/merge": "0.
|
|
68
|
-
"lucide-react": "^1.
|
|
65
|
+
"@adecore/editor": "0.40.0",
|
|
66
|
+
"@adecore/lsp": "0.40.0",
|
|
67
|
+
"@adecore/merge": "0.40.0",
|
|
68
|
+
"lucide-react": "^1.61.0",
|
|
69
69
|
"react-markdown": "^10.1.0",
|
|
70
70
|
"remark-breaks": "^4.0.0",
|
|
71
71
|
"remark-gfm": "^4.0.1",
|
|
72
|
-
"zustand": "^5.0.
|
|
72
|
+
"zustand": "^5.0.16"
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
|
-
"@adecore/ui": "^0.
|
|
75
|
+
"@adecore/ui": "^0.40.0",
|
|
76
76
|
"i18next": "^26.4.2",
|
|
77
77
|
"react": "^19.3.0",
|
|
78
78
|
"react-dom": "^19.3.0",
|
|
79
79
|
"react-i18next": "^17.0.15"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
|
-
"@adecore/ui": "0.
|
|
83
|
-
"@types/bun": "^1.4.
|
|
82
|
+
"@adecore/ui": "0.40.0",
|
|
83
|
+
"@types/bun": "^1.4.4",
|
|
84
84
|
"@types/react": "^19.3.0",
|
|
85
85
|
"@types/react-dom": "^19.3.0",
|
|
86
86
|
"typescript": "^7.0.2"
|
package/src/AnchoredPopup.tsx
CHANGED
|
@@ -25,9 +25,11 @@ export function AnchoredPopup({ rect, className, overflow = 'scroll', children,
|
|
|
25
25
|
// Every render, since the character moves when the editor scrolls and the card's own size follows its content.
|
|
26
26
|
useLayoutEffect(() => {
|
|
27
27
|
const popup = element.current;
|
|
28
|
+
|
|
28
29
|
if (popup === null) {
|
|
29
30
|
return;
|
|
30
31
|
}
|
|
32
|
+
|
|
31
33
|
popup.style.maxHeight = '';
|
|
32
34
|
const size = { width: popup.offsetWidth, height: popup.offsetHeight };
|
|
33
35
|
const placed = placePopup(rect, size, { width: window.innerWidth, height: window.innerHeight }, placement);
|
package/src/BaselineStatus.tsx
CHANGED
|
@@ -11,6 +11,7 @@ const BASELINE_MARKS: Record<BaselineLevel, { icon: typeof CircleCheck; color: s
|
|
|
11
11
|
/* Where a web feature stands across browsers, as one line under a CSS property. */
|
|
12
12
|
export function BaselineStatus({ baseline }: { baseline: Baseline }) {
|
|
13
13
|
const { icon, color } = BASELINE_MARKS[baseline.level];
|
|
14
|
+
|
|
14
15
|
return (
|
|
15
16
|
<div className="flex items-start gap-1.5 text-xs/[18px] text-text-muted select-text">
|
|
16
17
|
<Icon icon={icon} size={14} className={`mt-px shrink-0 ${color}`} />
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useId,
|
|
1
|
+
import { useEffect, useId, useRef, type ReactNode, type Ref } from 'react';
|
|
2
2
|
import { createPortal } from 'react-dom';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import { ChevronDown, ChevronLeft, ChevronRight, X } from 'lucide-react';
|
|
@@ -16,6 +16,7 @@ import { PathText } from './PathText.tsx';
|
|
|
16
16
|
import { Preview } from './PeekPanel.tsx';
|
|
17
17
|
import type { CallHierarchyView } from './popups.ts';
|
|
18
18
|
import { SymbolBadge } from './SymbolPicker.tsx';
|
|
19
|
+
import { useInputPopup } from './popup-focus.ts';
|
|
19
20
|
|
|
20
21
|
const INDENT = 14;
|
|
21
22
|
const BASE_INDENT = 8;
|
|
@@ -68,6 +69,7 @@ function SymbolRow({ language, row, active, id, ref }: { language: EditorLanguag
|
|
|
68
69
|
const { item } = row;
|
|
69
70
|
const path = relativeTo(fileUriToPath(item.uri) ?? item.uri, language.project.folder);
|
|
70
71
|
const tool = language.callHierarchy;
|
|
72
|
+
|
|
71
73
|
return (
|
|
72
74
|
<Line
|
|
73
75
|
ref={ref}
|
|
@@ -111,29 +113,30 @@ function Tree({ language, view }: { language: EditorLanguage; view: CallHierarch
|
|
|
111
113
|
const { t } = useTranslation('editor');
|
|
112
114
|
const treeId = useId();
|
|
113
115
|
const activeRow = useRef<HTMLDivElement>(null);
|
|
116
|
+
|
|
114
117
|
const idOf = (row: CallRow): string => `${treeId}-${row.id}`;
|
|
118
|
+
|
|
115
119
|
const activeId = view.rows.some((row) => row.id === view.active) ? `${treeId}-${view.active}` : undefined;
|
|
116
120
|
|
|
117
121
|
useEffect(() => {
|
|
118
122
|
activeRow.current?.scrollIntoView({ block: 'nearest' });
|
|
119
123
|
}, [view.active]);
|
|
120
124
|
|
|
121
|
-
|
|
122
|
-
useLayoutEffect(() => {
|
|
123
|
-
language.editor.setInputPopup({ controls: treeId, activeDescendant: activeId });
|
|
124
|
-
return () => language.editor.setInputPopup(null);
|
|
125
|
-
}, [language, treeId, activeId]);
|
|
125
|
+
useInputPopup(language, treeId, activeId);
|
|
126
126
|
|
|
127
127
|
return (
|
|
128
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">
|
|
129
129
|
{view.rows.map((row) => {
|
|
130
130
|
const active = row.id === view.active;
|
|
131
131
|
const ref = active ? activeRow : undefined;
|
|
132
|
+
|
|
132
133
|
if (row.kind === 'node') {
|
|
133
134
|
return <SymbolRow key={row.id} ref={ref} language={language} row={row} active={active} id={idOf(row)} />;
|
|
134
135
|
}
|
|
136
|
+
|
|
135
137
|
if (row.kind === 'more') {
|
|
136
138
|
const next = Math.min(row.hidden, CALL_LEVEL_LIMIT);
|
|
139
|
+
|
|
137
140
|
return (
|
|
138
141
|
<Line key={row.id} ref={ref} row={row} id={idOf(row)} active={active} onClick={() => language.callHierarchy.showMore(row.parent)}>
|
|
139
142
|
<span className="font-sans text-xs text-accent">
|
|
@@ -142,6 +145,7 @@ function Tree({ language, view }: { language: EditorLanguage; view: CallHierarch
|
|
|
142
145
|
</Line>
|
|
143
146
|
);
|
|
144
147
|
}
|
|
148
|
+
|
|
145
149
|
return (
|
|
146
150
|
<Line key={row.id} row={row} id={idOf(row)} active={false} onClick={() => language.callHierarchy.retry(row.parent)}>
|
|
147
151
|
<StatusText row={row} direction={view.direction} />
|
|
@@ -154,6 +158,7 @@ function Tree({ language, view }: { language: EditorLanguage; view: CallHierarch
|
|
|
154
158
|
|
|
155
159
|
function StatusText({ row, direction }: { row: Extract<CallRow, { kind: 'status' }>; direction: CallDirection }) {
|
|
156
160
|
const { t } = useTranslation('editor');
|
|
161
|
+
|
|
157
162
|
if (row.status === 'loading') {
|
|
158
163
|
return (
|
|
159
164
|
<span className="flex items-center gap-2 font-sans text-xs text-text-muted">
|
|
@@ -162,11 +167,13 @@ function StatusText({ row, direction }: { row: Extract<CallRow, { kind: 'status'
|
|
|
162
167
|
</span>
|
|
163
168
|
);
|
|
164
169
|
}
|
|
170
|
+
|
|
165
171
|
if (row.status === 'failed') {
|
|
166
172
|
return (
|
|
167
173
|
<span className="min-w-0 truncate font-sans text-xs text-status-error">{t('language.callHierarchy.retry', { message: row.message ?? '' })}</span>
|
|
168
174
|
);
|
|
169
175
|
}
|
|
176
|
+
|
|
170
177
|
return (
|
|
171
178
|
<span className="font-sans text-xs text-text-muted">
|
|
172
179
|
{t(direction === 'incoming' ? 'language.callHierarchy.noIncoming' : 'language.callHierarchy.noOutgoing')}
|
|
@@ -182,12 +189,15 @@ function StatusText({ row, direction }: { row: Extract<CallRow, { kind: 'status'
|
|
|
182
189
|
export function CallHierarchyPanel({ language, view }: { language: EditorLanguage; view: CallHierarchyView }) {
|
|
183
190
|
const { t } = useTranslation('editor');
|
|
184
191
|
const body = usePanelBody(language);
|
|
192
|
+
|
|
185
193
|
if (view.container === null) {
|
|
186
194
|
return null;
|
|
187
195
|
}
|
|
196
|
+
|
|
188
197
|
const folder = language.project.folder;
|
|
189
198
|
const path = view.preview === null ? null : fileUriToPath(view.preview.uri);
|
|
190
199
|
const shown = path === null ? '' : folderOf(relativeTo(path, folder));
|
|
200
|
+
|
|
191
201
|
return createPortal(
|
|
192
202
|
<div
|
|
193
203
|
className="flex flex-col bg-surface pt-[2px] font-sans text-xs text-text"
|
package/src/ChangeReview.tsx
CHANGED
|
@@ -14,15 +14,20 @@ export interface ChangeReviewProps {
|
|
|
14
14
|
|
|
15
15
|
export function ChangeReview({ editor, selected, proposal, startLine = 1, actions, className, label }: ChangeReviewProps) {
|
|
16
16
|
const code = useRef<HTMLDivElement>(null);
|
|
17
|
+
|
|
17
18
|
useLayoutEffect(() => {
|
|
18
19
|
const element = code.current;
|
|
20
|
+
|
|
19
21
|
if (element === null) {
|
|
20
22
|
return;
|
|
21
23
|
}
|
|
24
|
+
|
|
22
25
|
element.replaceChildren();
|
|
26
|
+
|
|
23
27
|
for (const segment of diffSegments(selected, proposal, startLine)) {
|
|
24
28
|
const row = element.ownerDocument.createElement('div');
|
|
25
29
|
element.append(row);
|
|
30
|
+
|
|
26
31
|
editor.renderCode(row, segment.text, {
|
|
27
32
|
firstLine: segment.firstLine,
|
|
28
33
|
...(segment.kind === 'same'
|
|
@@ -32,8 +37,10 @@ export function ChangeReview({ editor, selected, proposal, startLine = 1, action
|
|
|
32
37
|
emphasis: emphasisOf(segment)
|
|
33
38
|
});
|
|
34
39
|
}
|
|
40
|
+
|
|
35
41
|
return () => element.replaceChildren();
|
|
36
42
|
}, [editor, selected, proposal, startLine]);
|
|
43
|
+
|
|
37
44
|
return (
|
|
38
45
|
<section className={className} aria-label={label}>
|
|
39
46
|
<div ref={code} />
|
package/src/CodeAuthorsCard.tsx
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { useMemo } from 'react';
|
|
2
1
|
import { useTranslation } from 'react-i18next';
|
|
3
2
|
import { GitCommitHorizontal, User, Users } from 'lucide-react';
|
|
4
3
|
import { Button, Icon, Popover, Separator, Tooltip, useNow } from '@adecore/ui';
|
|
@@ -34,10 +33,8 @@ export function LatestCommit({ commit }: { commit: GitBlameCommit }) {
|
|
|
34
33
|
export function CodeAuthorsCard({ language, view }: { language: EditorLanguage; view: AuthorsView }) {
|
|
35
34
|
const { t } = useTranslation('editor');
|
|
36
35
|
const { authors, uncommittedLines, latest } = view.authorship;
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
return { getBoundingClientRect: () => new DOMRect(left, top, right - left, bottom - top) };
|
|
40
|
-
}, [view.anchor]);
|
|
36
|
+
const { left, top, right, bottom } = view.anchor;
|
|
37
|
+
const anchor = { getBoundingClientRect: () => new DOMRect(left, top, right - left, bottom - top) };
|
|
41
38
|
|
|
42
39
|
return (
|
|
43
40
|
<Popover.Root open onOpenChange={(open, details) => !open && language.codeVision.closeAuthors(details.reason === 'escape-key')}>
|
package/src/CompletionPopup.tsx
CHANGED
|
@@ -4,36 +4,34 @@ import { useTranslation } from 'react-i18next';
|
|
|
4
4
|
import { formatShortcut } from '@adecore/ui';
|
|
5
5
|
import type { EditorRect } from '@adecore/editor';
|
|
6
6
|
import type { EditorLanguage } from './editor-language.ts';
|
|
7
|
-
import { kindLetterOf, kindToneOf, rowWindow
|
|
7
|
+
import { kindLetterOf, kindToneOf, rowWindow } from './completion-model.ts';
|
|
8
8
|
import { SourceLine, SymbolSections } from './HoverSections.tsx';
|
|
9
9
|
import { PathText } from './PathText.tsx';
|
|
10
|
-
import { keepFocusOutOfList } from './popup-focus.ts';
|
|
10
|
+
import { keepFocusOutOfList, useInputPopup } from './popup-focus.ts';
|
|
11
11
|
import { placeBeside, placePopup } from './popup-placement.ts';
|
|
12
12
|
import type { CompletionView } from './popups.ts';
|
|
13
|
-
|
|
14
|
-
const TONES: Record<KindTone, string> = {
|
|
15
|
-
callable: 'bg-accent-soft text-accent',
|
|
16
|
-
value: 'bg-status-running/15 text-status-running',
|
|
17
|
-
type: 'bg-status-idle/15 text-status-idle',
|
|
18
|
-
other: 'bg-surface-hover text-text-muted'
|
|
19
|
-
};
|
|
13
|
+
import { TONE_CLASSES } from './symbol-picker-model.ts';
|
|
20
14
|
|
|
21
15
|
/* The label with the characters that match what was typed set apart. */
|
|
22
16
|
function MarkedLabel({ label, matches }: { label: string; matches: readonly number[] }) {
|
|
23
17
|
if (matches.length === 0) {
|
|
24
18
|
return <>{label}</>;
|
|
25
19
|
}
|
|
20
|
+
|
|
26
21
|
const marked = new Set(matches);
|
|
27
22
|
const runs: { text: string; marked: boolean }[] = [];
|
|
23
|
+
|
|
28
24
|
for (let at = 0; at < label.length; at++) {
|
|
29
25
|
const isMarked = marked.has(at);
|
|
30
26
|
const last = runs[runs.length - 1];
|
|
27
|
+
|
|
31
28
|
if (last !== undefined && last.marked === isMarked) {
|
|
32
29
|
last.text += label[at];
|
|
33
30
|
} else {
|
|
34
31
|
runs.push({ text: label[at]!, marked: isMarked });
|
|
35
32
|
}
|
|
36
33
|
}
|
|
34
|
+
|
|
37
35
|
return (
|
|
38
36
|
<>
|
|
39
37
|
{runs.map((run, index) =>
|
|
@@ -52,10 +50,13 @@ function MarkedLabel({ label, matches }: { label: string; matches: readonly numb
|
|
|
52
50
|
const CARD = 'fixed top-0 left-0 z-(--z-popup) overflow-hidden rounded-lg border border-border bg-surface-raised text-text shadow-(--float-shadow)';
|
|
53
51
|
const ROW_HEIGHT = 24;
|
|
54
52
|
const VISIBLE_ROWS = 8;
|
|
53
|
+
|
|
55
54
|
/* Rows drawn past the visible ones on each side, so a wheel turn lands on rows that exist. */
|
|
56
55
|
const OVERSCAN_ROWS = 6;
|
|
56
|
+
|
|
57
57
|
/* The padding the listbox has around its rows. */
|
|
58
58
|
const LIST_PADDING = 4;
|
|
59
|
+
|
|
59
60
|
/* What a row takes besides its name and namespace: padding, the kind badge and the gaps. */
|
|
60
61
|
const ROW_CHROME = 72;
|
|
61
62
|
|
|
@@ -77,6 +78,7 @@ export function CompletionPopup({ language, view, rect }: { language: EditorLang
|
|
|
77
78
|
// Only the rows near the visible ones are drawn: a list of 150 is mostly out of sight, and every keystroke makes it again.
|
|
78
79
|
const [listTop, setListTop] = useState(0);
|
|
79
80
|
const need = Math.max(0, ...view.rows.slice(0, VISIBLE_ROWS * 2).map((row) => row.label.length + 2 + row.description.length));
|
|
81
|
+
|
|
80
82
|
if (need > widest) {
|
|
81
83
|
setWidest(need);
|
|
82
84
|
}
|
|
@@ -84,13 +86,16 @@ export function CompletionPopup({ language, view, rect }: { language: EditorLang
|
|
|
84
86
|
// Every render: the word moves when the editor scrolls, and the list and the documentation change size with what they hold.
|
|
85
87
|
useLayoutEffect(() => {
|
|
86
88
|
const card = list.current;
|
|
89
|
+
|
|
87
90
|
if (card === null) {
|
|
88
91
|
return;
|
|
89
92
|
}
|
|
93
|
+
|
|
90
94
|
const viewport = {
|
|
91
95
|
width: window.innerWidth,
|
|
92
96
|
height: window.innerHeight
|
|
93
97
|
};
|
|
98
|
+
|
|
94
99
|
const side = docs.current;
|
|
95
100
|
// Every size is read before the first write, so the page is laid out once and not after each of them.
|
|
96
101
|
const size = { width: card.offsetWidth, height: card.offsetHeight };
|
|
@@ -99,12 +104,14 @@ export function CompletionPopup({ language, view, rect }: { language: EditorLang
|
|
|
99
104
|
card.style.left = `${Math.round(placed.left)}px`;
|
|
100
105
|
card.style.top = `${Math.round(placed.top)}px`;
|
|
101
106
|
card.style.visibility = 'visible';
|
|
107
|
+
|
|
102
108
|
if (side !== null && sideSize !== null) {
|
|
103
109
|
const box = {
|
|
104
110
|
left: Math.round(placed.left),
|
|
105
111
|
top: Math.round(placed.top),
|
|
106
112
|
...size
|
|
107
113
|
};
|
|
114
|
+
|
|
108
115
|
const beside = placeBeside(box, sideSize, viewport);
|
|
109
116
|
side.style.left = `${Math.round(beside.left)}px`;
|
|
110
117
|
side.style.top = `${Math.round(beside.top)}px`;
|
|
@@ -119,14 +126,12 @@ export function CompletionPopup({ language, view, rect }: { language: EditorLang
|
|
|
119
126
|
|
|
120
127
|
const total = view.rows.length;
|
|
121
128
|
const { first, last } = rowWindow(listTop, view.active, total, ROW_HEIGHT, VISIBLE_ROWS, OVERSCAN_ROWS, LIST_PADDING);
|
|
129
|
+
|
|
122
130
|
const optionId = (index: number): string => `${listId}-option-${index}`;
|
|
131
|
+
|
|
123
132
|
const activeId = view.active >= first && view.active < last ? optionId(view.active) : undefined;
|
|
124
133
|
|
|
125
|
-
|
|
126
|
-
useLayoutEffect(() => {
|
|
127
|
-
language.editor.setInputPopup({ controls: listId, activeDescendant: activeId });
|
|
128
|
-
return () => language.editor.setInputPopup(null);
|
|
129
|
-
}, [language, listId, activeId]);
|
|
134
|
+
useInputPopup(language, listId, activeId);
|
|
130
135
|
|
|
131
136
|
return createPortal(
|
|
132
137
|
<>
|
|
@@ -155,6 +160,7 @@ export function CompletionPopup({ language, view, rect }: { language: EditorLang
|
|
|
155
160
|
<div aria-hidden style={{ height: first * ROW_HEIGHT }} />
|
|
156
161
|
{view.rows.slice(first, last).map((row, offset) => {
|
|
157
162
|
const index = first + offset;
|
|
163
|
+
|
|
158
164
|
return (
|
|
159
165
|
<button
|
|
160
166
|
key={`${index}:${row.label}`}
|
|
@@ -171,7 +177,7 @@ export function CompletionPopup({ language, view, rect }: { language: EditorLang
|
|
|
171
177
|
onClick={() => void language.completion.accept(false, index)}
|
|
172
178
|
>
|
|
173
179
|
<span
|
|
174
|
-
className={`grid size-4 shrink-0 place-items-center rounded-sm font-mono text-xs leading-none font-semibold ${
|
|
180
|
+
className={`grid size-4 shrink-0 place-items-center rounded-sm font-mono text-xs leading-none font-semibold ${TONE_CLASSES[kindToneOf(row.kind)]}`}
|
|
175
181
|
>
|
|
176
182
|
{kindLetterOf(row.kind)}
|
|
177
183
|
</span>
|