@adecore/editor-react 0.39.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
|
@@ -10,6 +10,7 @@ export interface ParsedDocComment {
|
|
|
10
10
|
}
|
|
11
11
|
export declare const DOC_LINK_PREFIX = "#doc-link:";
|
|
12
12
|
export declare function decodeEntities(text: string): string;
|
|
13
|
+
export declare function dedent(text: string): string;
|
|
13
14
|
export declare function safeAddress(address: string): string | null;
|
|
14
15
|
export declare function htmlToMarkdown(source: string): string;
|
|
15
16
|
export declare function inlineTagsToMarkdown(markdown: string): string;
|
|
@@ -170,7 +170,7 @@ export function decodeEntities(text) {
|
|
|
170
170
|
return ENTITIES[code.toLowerCase()] ?? match;
|
|
171
171
|
});
|
|
172
172
|
}
|
|
173
|
-
function dedent(text) {
|
|
173
|
+
export function dedent(text) {
|
|
174
174
|
const lines = text.replace(/^\n+|\s+$/g, '').split('\n');
|
|
175
175
|
const indents = lines.filter(line => line.trim() !== '').map(line => line.length - line.trimStart().length);
|
|
176
176
|
const common = indents.length === 0 ? 0 : Math.min(...indents);
|
package/dist/folding.js
CHANGED
package/dist/format-on-type.js
CHANGED
|
@@ -10,17 +10,13 @@ function overlaps(edits) {
|
|
|
10
10
|
return ordered.some((edit, index) => index > 0 && comparePositions(ordered[index - 1].range.end, edit.range.start) > 0);
|
|
11
11
|
}
|
|
12
12
|
/*
|
|
13
|
-
* Formatting while typing: after
|
|
14
|
-
*
|
|
15
|
-
* edits are applied.
|
|
13
|
+
* Formatting while typing: after one of the server's trigger characters is typed, the server is asked what
|
|
14
|
+
* to change around the caret, and its edits join the undo step of the typed character.
|
|
16
15
|
*
|
|
17
|
-
* Only typing counts:
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* only while the text is exactly what it was asked about, and the caret where it was.
|
|
22
|
-
*
|
|
23
|
-
* The edits join the undo step of the typed character, so one undo takes both back. Off by default; a host enables it explicitly with `configure(true)`.
|
|
16
|
+
* Only typing counts: the trigger character alone, the closer the editor moved back to its opener's
|
|
17
|
+
* indentation, or a line break with its indentation for `\n`. A paste, undo, a completion, an external change,
|
|
18
|
+
* a second caret, a read-only editor or a snippet session (whose tab stops an edit could move) asks nothing.
|
|
19
|
+
* An answer is applied only while the text and the caret are what it was asked about. Off until `configure(true)`.
|
|
24
20
|
*/
|
|
25
21
|
export class FormatOnTypeFeature {
|
|
26
22
|
language;
|
package/dist/hover-content.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { EditorRange } from '@adecore/editor';
|
|
2
1
|
import type { Hover, Location, NavigationResult } from '@adecore/lsp';
|
|
3
2
|
export interface SignatureBlock {
|
|
4
3
|
readonly language: string;
|
|
@@ -13,7 +12,6 @@ export declare function hoverTextOf(hover: Hover): HoverText;
|
|
|
13
12
|
export declare function escapeMarkdown(text: string): string;
|
|
14
13
|
export declare function isEmptyHover(text: HoverText): boolean;
|
|
15
14
|
export declare function locationsOf(result: NavigationResult): Location[];
|
|
16
|
-
export declare function sameRange(left: EditorRange, right: EditorRange): boolean;
|
|
17
15
|
export interface DocTag {
|
|
18
16
|
readonly name: string;
|
|
19
17
|
readonly markdown: string;
|
package/dist/hover-content.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { dedent } from './doc-comment-model.js';
|
|
1
2
|
const FENCE = /^```([\w+#.-]*)[^\n]*\n([\s\S]*?)\n```[ \t]*(?:\n|$)/;
|
|
2
3
|
/*
|
|
3
4
|
* The leading fenced blocks of a markdown text as signatures, and what follows them, minus the rule
|
|
@@ -72,9 +73,6 @@ export function locationsOf(result) {
|
|
|
72
73
|
range: entry.targetSelectionRange
|
|
73
74
|
} : entry);
|
|
74
75
|
}
|
|
75
|
-
export function sameRange(left, right) {
|
|
76
|
-
return left.start.line === right.start.line && left.start.character === right.start.character && left.end.line === right.end.line && left.end.character === right.end.character;
|
|
77
|
-
}
|
|
78
76
|
const TAG_LINE = /^(?:_@([\w-]+)_|\*@([\w-]+)\*|@([\w-]+))(?:\s+|$)([\s\S]*)$/;
|
|
79
77
|
/* Tags whose text is a block of its own, which a row of the tag list has no room for. */
|
|
80
78
|
const BLOCK_TAGS = new Set(['example']);
|
|
@@ -145,12 +143,6 @@ const INLINE_TAG = /\{@(see|link|linkplain)\s+([^}\s]+)(?:\s+([^}]+?))?\s*\}/g;
|
|
|
145
143
|
function decodeEntities(text) {
|
|
146
144
|
return text.replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/�?39;/g, "'").replace(/&/g, '&');
|
|
147
145
|
}
|
|
148
|
-
function dedent(text) {
|
|
149
|
-
const lines = text.replace(/^\n+|\s+$/g, '').split('\n');
|
|
150
|
-
const indents = lines.filter(line => line.trim() !== '').map(line => line.length - line.trimStart().length);
|
|
151
|
-
const common = indents.length === 0 ? 0 : Math.min(...indents);
|
|
152
|
-
return lines.map(line => line.slice(common)).join('\n');
|
|
153
|
-
}
|
|
154
146
|
/*
|
|
155
147
|
* What a docblock writes for a reader of its source, written for one of a hover: a `<code>` or `<pre>`
|
|
156
148
|
* example as a block in the symbol's language, and an inline `{@see Name}` or `{@link Name label}` as
|
package/dist/inlay-hints.d.ts
CHANGED
|
@@ -6,7 +6,6 @@ import { type Timers } from './timers.ts';
|
|
|
6
6
|
export declare function hintLabelOf(hint: InlayHint): string;
|
|
7
7
|
export declare function drawableHints(hints: readonly InlayHint[]): InlayHint[];
|
|
8
8
|
export declare function editorHintOf(hint: InlayHint, id: string): EditorInlayHint;
|
|
9
|
-
export declare function editorHintsOf(hints: readonly InlayHint[]): EditorInlayHint[];
|
|
10
9
|
export declare function withMargin(visible: EditorRange, endOfDocument: EditorPosition, endOfLine: (line: number) => EditorPosition): EditorRange;
|
|
11
10
|
export declare function covers(asked: EditorRange, visible: EditorRange): boolean;
|
|
12
11
|
export declare function tooltipTextOf(tooltip: string | MarkupContent | undefined): HoverText | null;
|
package/dist/inlay-hints.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { followModKey } from './definition-link.js';
|
|
2
|
+
import { comparePositions } from './diagnostics-model.js';
|
|
1
3
|
import { hoverTextOf, isEmptyHover } from './hover-content.js';
|
|
2
4
|
import { SHOW_DELAY_MS } from './hover.js';
|
|
3
5
|
import { messageOf } from './error-message.js';
|
|
@@ -12,13 +14,11 @@ const PAUSE_MS = 300;
|
|
|
12
14
|
const DOUBLE_PRESS_MS = 500;
|
|
13
15
|
/* Lines asked for above and below what is in view, so a little scrolling stays inside what is already drawn. */
|
|
14
16
|
const MARGIN_LINES = 60;
|
|
15
|
-
/* A hint's label is a string or a list of parts, which are read as one text. */
|
|
16
17
|
export function hintLabelOf(hint) {
|
|
17
18
|
return typeof hint.label === 'string' ? hint.label : hint.label.map(part => part.value).join('');
|
|
18
19
|
}
|
|
19
|
-
/* The hints worth drawing, in the order of the text. */
|
|
20
20
|
export function drawableHints(hints) {
|
|
21
|
-
return hints.filter(hint => hintLabelOf(hint) !== '').sort((left, right) => left.position
|
|
21
|
+
return hints.filter(hint => hintLabelOf(hint) !== '').sort((left, right) => comparePositions(left.position, right.position));
|
|
22
22
|
}
|
|
23
23
|
/* A hint as the editor draws it: a label with parts keeps them apart so each can be pointed at. */
|
|
24
24
|
export function editorHintOf(hint, id) {
|
|
@@ -37,10 +37,6 @@ export function editorHintOf(hint, id) {
|
|
|
37
37
|
} : {})
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
|
-
export function editorHintsOf(hints) {
|
|
41
|
-
return drawableHints(hints).map((hint, index) => editorHintOf(hint, `hint-${index}`));
|
|
42
|
-
}
|
|
43
|
-
/* The lines in view with a margin of lines around them, from the start of the first to the end of the last. */
|
|
44
40
|
export function withMargin(visible, endOfDocument, endOfLine) {
|
|
45
41
|
const first = Math.max(0, visible.start.line - MARGIN_LINES);
|
|
46
42
|
const last = Math.min(endOfDocument.line, visible.end.line + MARGIN_LINES);
|
|
@@ -52,7 +48,6 @@ export function withMargin(visible, endOfDocument, endOfLine) {
|
|
|
52
48
|
end: last === endOfDocument.line ? endOfDocument : endOfLine(last)
|
|
53
49
|
};
|
|
54
50
|
}
|
|
55
|
-
/* Whether what was asked for already holds every line in view. */
|
|
56
51
|
export function covers(asked, visible) {
|
|
57
52
|
return asked.start.line <= visible.start.line && asked.end.line >= visible.end.line;
|
|
58
53
|
}
|
|
@@ -69,7 +64,6 @@ export function tooltipTextOf(tooltip) {
|
|
|
69
64
|
});
|
|
70
65
|
return isEmptyHover(text) ? null : text;
|
|
71
66
|
}
|
|
72
|
-
/* One part of a hint's label, or null for a hint whose label is a string. */
|
|
73
67
|
export function partOf(hint, index) {
|
|
74
68
|
return index === null || typeof hint.label === 'string' ? null : hint.label[index] ?? null;
|
|
75
69
|
}
|
|
@@ -94,7 +88,7 @@ export function lacksInfo(hint, index) {
|
|
|
94
88
|
export function editsFit(editor, edits) {
|
|
95
89
|
const end = editor.positionAt(Number.MAX_SAFE_INTEGER);
|
|
96
90
|
const inside = position => Number.isInteger(position.line) && Number.isInteger(position.character) && position.line >= 0 && position.character >= 0 && position.line <= end.line && position.character <= lineTextOf(editor, position.line).length;
|
|
97
|
-
const before = (left, right) => left
|
|
91
|
+
const before = (left, right) => comparePositions(left, right) <= 0;
|
|
98
92
|
const ordered = [...edits].sort((left, right) => before(left.range.start, right.range.start) ? -1 : 1);
|
|
99
93
|
return ordered.every((edit, index) => {
|
|
100
94
|
const next = ordered[index + 1];
|
|
@@ -123,17 +117,12 @@ export function actionOf(hint, click) {
|
|
|
123
117
|
}
|
|
124
118
|
/*
|
|
125
119
|
* Types and parameter names the servers infer, asked for the lines in view and a margin around them. The
|
|
126
|
-
* last ones stay until the answer comes, so nothing blinks while typing
|
|
127
|
-
* view leaves the lines that were asked for.
|
|
120
|
+
* last ones stay until the answer comes, so nothing blinks while typing.
|
|
128
121
|
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
* offers nothing (yet) moves the caret as beside the text. A finger has no hover, so a tap shows the
|
|
134
|
-
* tooltip and does nothing more. All of it is for the pointer: the hints are not part
|
|
135
|
-
* of the text and stay out of the keyboard's way, and Quick Info on the caret shows the tooltip of a hint that
|
|
136
|
-
* sits there.
|
|
122
|
+
* Mod+click on a part follows its place, a click runs its command and a double click inserts its text edits
|
|
123
|
+
* as one undo step, never together with the command. A press on a hint that offers nothing (yet) moves the
|
|
124
|
+
* caret; a tap only shows the tooltip. The hints stay out of the keyboard's way: Quick Info on the caret
|
|
125
|
+
* shows the tooltip of a hint that sits there.
|
|
137
126
|
*/
|
|
138
127
|
export class InlayHintsFeature {
|
|
139
128
|
language;
|
|
@@ -203,23 +192,7 @@ export class InlayHintsFeature {
|
|
|
203
192
|
if (editor.onInlayClick !== undefined) {
|
|
204
193
|
offs.push(editor.onInlayClick(click => this.pressed(click)));
|
|
205
194
|
}
|
|
206
|
-
|
|
207
|
-
const modKey = language.apple ? 'Meta' : 'Control';
|
|
208
|
-
const keys = event => {
|
|
209
|
-
if (event.key === modKey) {
|
|
210
|
-
this.setModHeld(event.type === 'keydown');
|
|
211
|
-
}
|
|
212
|
-
};
|
|
213
|
-
const lost = () => this.setModHeld(false);
|
|
214
|
-
window.addEventListener('keydown', keys);
|
|
215
|
-
window.addEventListener('keyup', keys);
|
|
216
|
-
window.addEventListener('blur', lost);
|
|
217
|
-
offs.push(() => {
|
|
218
|
-
window.removeEventListener('keydown', keys);
|
|
219
|
-
window.removeEventListener('keyup', keys);
|
|
220
|
-
window.removeEventListener('blur', lost);
|
|
221
|
-
});
|
|
222
|
-
}
|
|
195
|
+
offs.push(followModKey(language.apple, held => this.setModHeld(held)));
|
|
223
196
|
language.onDispose(() => {
|
|
224
197
|
for (const off of offs) {
|
|
225
198
|
off();
|
|
@@ -240,7 +213,7 @@ export class InlayHintsFeature {
|
|
|
240
213
|
}
|
|
241
214
|
/* The tooltip of a hint that sits at a position, as a card; false when there is none. This is how the keyboard reaches it. */
|
|
242
215
|
async showTooltipAt(position) {
|
|
243
|
-
const found = [...this.entries].find(([, entry]) => entry.epoch === this.epoch &&
|
|
216
|
+
const found = [...this.entries].find(([, entry]) => entry.epoch === this.epoch && comparePositions(entry.hint.position, position) === 0);
|
|
244
217
|
if (found === undefined) {
|
|
245
218
|
return false;
|
|
246
219
|
}
|
|
@@ -447,7 +420,4 @@ export class InlayHintsFeature {
|
|
|
447
420
|
});
|
|
448
421
|
}
|
|
449
422
|
}
|
|
450
|
-
}
|
|
451
|
-
function sameSpot(left, right) {
|
|
452
|
-
return left.line === right.line && left.character === right.character;
|
|
453
423
|
}
|
package/dist/navigation.js
CHANGED
|
@@ -5,6 +5,7 @@ import { rangeHolds } from './diagnostics-model.js';
|
|
|
5
5
|
import { placesOfName } from './symbol-links.js';
|
|
6
6
|
import { isShortcut } from './shortcut-keys.js';
|
|
7
7
|
import { messageOf } from './error-message.js';
|
|
8
|
+
import { placeKey, uniqueBy } from './unique.js';
|
|
8
9
|
const METHODS = {
|
|
9
10
|
definition: 'textDocument/definition',
|
|
10
11
|
declaration: 'textDocument/declaration',
|
|
@@ -21,9 +22,8 @@ const SHORTCUT_KINDS = [['definition', 'goToDefinition'], ['typeDefinition', 'go
|
|
|
21
22
|
const TOAST_ID = 'language-navigation';
|
|
22
23
|
/* The places of a result, each once: the two servers of a Vue file may both know the same one. `target` is the whole declaration when the server gave it. */
|
|
23
24
|
export function uniquePlaces(result) {
|
|
24
|
-
const seen = new Set();
|
|
25
25
|
const list = result === null ? [] : Array.isArray(result) ? result : [result];
|
|
26
|
-
return list.map(entry => 'targetUri' in entry ? {
|
|
26
|
+
return uniqueBy(list.map(entry => 'targetUri' in entry ? {
|
|
27
27
|
location: {
|
|
28
28
|
uri: entry.targetUri,
|
|
29
29
|
range: entry.targetSelectionRange
|
|
@@ -32,19 +32,9 @@ export function uniquePlaces(result) {
|
|
|
32
32
|
} : {
|
|
33
33
|
location: entry,
|
|
34
34
|
target: null
|
|
35
|
-
})
|
|
35
|
+
}), ({
|
|
36
36
|
location
|
|
37
|
-
}) =>
|
|
38
|
-
const {
|
|
39
|
-
start
|
|
40
|
-
} = location.range;
|
|
41
|
-
const key = `${location.uri}\0${start.line}\0${start.character}`;
|
|
42
|
-
if (seen.has(key)) {
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
seen.add(key);
|
|
46
|
-
return true;
|
|
47
|
-
});
|
|
37
|
+
}) => placeKey(location));
|
|
48
38
|
}
|
|
49
39
|
/* The places of a result, each once. */
|
|
50
40
|
export function uniqueLocations(result) {
|
package/dist/peek.js
CHANGED
|
@@ -5,6 +5,7 @@ import { PEEK_READ_FILES, definitionSnippetOf, peekFilesOf, snippetOf } from './
|
|
|
5
5
|
import { isShortcut } from './shortcut-keys.js';
|
|
6
6
|
import { messageOf } from './error-message.js';
|
|
7
7
|
import { realTimers, revealRow } from './timers.js';
|
|
8
|
+
import { placeKey, uniqueBy } from './unique.js';
|
|
8
9
|
const TOAST_ID = 'language-peek';
|
|
9
10
|
/* The block as the panel draws it: a hatched band, the header, eight lines of code and a hatched band, until the editor has measured it. */
|
|
10
11
|
export const PEEK_HEIGHT = 241;
|
|
@@ -20,9 +21,6 @@ const KINDS = {
|
|
|
20
21
|
none: 'noDefinition'
|
|
21
22
|
}
|
|
22
23
|
};
|
|
23
|
-
function placeKey(location) {
|
|
24
|
-
return `${location.uri}\0${location.range.start.line}\0${location.range.start.character}`;
|
|
25
|
-
}
|
|
26
24
|
/*
|
|
27
25
|
* The references of the name at the caret, between the lines of the file under the line it is on. The
|
|
28
26
|
* editor keeps the keyboard: arrows pick a reference, Enter goes to it and Escape closes the peek; typing
|
|
@@ -95,15 +93,7 @@ export class PeekFeature {
|
|
|
95
93
|
const {
|
|
96
94
|
targets
|
|
97
95
|
} = found;
|
|
98
|
-
const
|
|
99
|
-
const unique = found.locations.filter(location => {
|
|
100
|
-
const key = placeKey(location);
|
|
101
|
-
if (seen.has(key)) {
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
seen.add(key);
|
|
105
|
-
return true;
|
|
106
|
-
});
|
|
96
|
+
const unique = uniqueBy(found.locations, placeKey);
|
|
107
97
|
if (unique.length === 0) {
|
|
108
98
|
this.tell(this.say(KINDS[kind].none));
|
|
109
99
|
return;
|
package/dist/popup-focus.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type FocusEvent, type MouseEvent } from 'react';
|
|
2
|
+
import type { EditorLanguage } from './editor-language.ts';
|
|
2
3
|
export declare function keepFocusOutOfList(focusBack: () => void): {
|
|
3
4
|
onMouseDown(event: MouseEvent): void;
|
|
4
5
|
onFocus(event: FocusEvent): void;
|
|
5
6
|
};
|
|
7
|
+
export declare function useInputPopup(language: EditorLanguage, controls: string, activeDescendant: string | undefined): void;
|
package/dist/popup-focus.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { c as _c } from "react/compiler-runtime";
|
|
2
|
+
import { useLayoutEffect } from 'react';
|
|
1
3
|
/*
|
|
2
4
|
* The handlers for a popup list whose rows the keyboard never enters: the text it belongs to keeps the
|
|
3
5
|
* focus, and the active row is only `aria-activedescendant` or a highlight. Chromium reports that row as
|
|
@@ -9,4 +11,35 @@ export function keepFocusOutOfList(focusBack) {
|
|
|
9
11
|
onMouseDown: event => event.preventDefault(),
|
|
10
12
|
onFocus: () => focusBack()
|
|
11
13
|
};
|
|
14
|
+
}
|
|
15
|
+
/* The focus stays in the text, so the input is what names the popup's list and its active option. */
|
|
16
|
+
export function useInputPopup(language, controls, activeDescendant) {
|
|
17
|
+
const $ = _c(8);
|
|
18
|
+
let t0;
|
|
19
|
+
if ($[0] !== activeDescendant || $[1] !== controls || $[2] !== language.editor) {
|
|
20
|
+
t0 = () => {
|
|
21
|
+
language.editor.setInputPopup({
|
|
22
|
+
controls,
|
|
23
|
+
activeDescendant
|
|
24
|
+
});
|
|
25
|
+
return () => language.editor.setInputPopup(null);
|
|
26
|
+
};
|
|
27
|
+
$[0] = activeDescendant;
|
|
28
|
+
$[1] = controls;
|
|
29
|
+
$[2] = language.editor;
|
|
30
|
+
$[3] = t0;
|
|
31
|
+
} else {
|
|
32
|
+
t0 = $[3];
|
|
33
|
+
}
|
|
34
|
+
let t1;
|
|
35
|
+
if ($[4] !== activeDescendant || $[5] !== controls || $[6] !== language) {
|
|
36
|
+
t1 = [language, controls, activeDescendant];
|
|
37
|
+
$[4] = activeDescendant;
|
|
38
|
+
$[5] = controls;
|
|
39
|
+
$[6] = language;
|
|
40
|
+
$[7] = t1;
|
|
41
|
+
} else {
|
|
42
|
+
t1 = $[7];
|
|
43
|
+
}
|
|
44
|
+
useLayoutEffect(t0, t1);
|
|
12
45
|
}
|
package/dist/recent-choices.d.ts
CHANGED
package/dist/recent-choices.js
CHANGED
package/dist/rendered-docs.d.ts
CHANGED
|
@@ -39,6 +39,7 @@ export declare class RenderedDocsFeature {
|
|
|
39
39
|
isRendered(comment: DocComment): boolean;
|
|
40
40
|
toggle(): boolean;
|
|
41
41
|
toggleAt(startLine: number): boolean;
|
|
42
|
+
private switch;
|
|
42
43
|
entries(): readonly RenderedDocEntry[];
|
|
43
44
|
getVersion(): number;
|
|
44
45
|
getEntries(): readonly RenderedDocEntry[];
|
package/dist/rendered-docs.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { findDocComments } from '@adecore/editor';
|
|
2
|
+
import { comparePositions } from './diagnostics-model.js';
|
|
2
3
|
import { KEY_PRIORITY } from './key-priority.js';
|
|
3
4
|
import { parseDocComment } from './doc-comment-model.js';
|
|
5
|
+
import { lineTextOf } from './line-text.js';
|
|
4
6
|
import { isShortcut } from './shortcut-keys.js';
|
|
5
7
|
import { realTimers } from './timers.js';
|
|
6
8
|
const OWNER = 'rendered-docs';
|
|
@@ -22,7 +24,7 @@ function touches(range, comment) {
|
|
|
22
24
|
if (range.end.line < comment.startLine || range.start.line > comment.endLine) {
|
|
23
25
|
return false;
|
|
24
26
|
}
|
|
25
|
-
const empty = range.start
|
|
27
|
+
const empty = comparePositions(range.start, range.end) === 0;
|
|
26
28
|
if (!empty && range.end.line === comment.startLine && range.end.character === 0) {
|
|
27
29
|
return false;
|
|
28
30
|
}
|
|
@@ -30,13 +32,13 @@ function touches(range, comment) {
|
|
|
30
32
|
}
|
|
31
33
|
/* The first words of what a comment says, to tell its row from the others for a person who hears it. */
|
|
32
34
|
function summaryOf(body) {
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
return
|
|
35
|
+
const lines = body.split('\n');
|
|
36
|
+
const lead = lines.findIndex(line => /^\s*@/.test(line));
|
|
37
|
+
const text = (lead === -1 ? body : lines.slice(0, lead).join('\n')).replace(/[`*_#>[\]()]/g, ' ');
|
|
38
|
+
return text.split(/\s+/).filter(word => word !== '').slice(0, SUMMARY_WORDS).join(' ');
|
|
37
39
|
}
|
|
38
40
|
function samePlace(range, position) {
|
|
39
|
-
return range !== null && range.start
|
|
41
|
+
return range !== null && comparePositions(range.start, position) === 0;
|
|
40
42
|
}
|
|
41
43
|
/*
|
|
42
44
|
* Documentation comments drawn as formatted text in place of their lines, the way the Reader mode of the
|
|
@@ -105,10 +107,7 @@ export class RenderedDocsFeature {
|
|
|
105
107
|
}
|
|
106
108
|
this.timers.clear(this.settleTimer);
|
|
107
109
|
this.timers.clear(this.caretTimer);
|
|
108
|
-
|
|
109
|
-
override.tracked.dispose();
|
|
110
|
-
}
|
|
111
|
-
this.overrides = [];
|
|
110
|
+
this.clearOverrides();
|
|
112
111
|
editor.setReplacements?.([], OWNER);
|
|
113
112
|
editor.setGutterMarkers([], OWNER);
|
|
114
113
|
this.containers.clear();
|
|
@@ -141,17 +140,14 @@ export class RenderedDocsFeature {
|
|
|
141
140
|
/* Switches the comment the caret is in between formatted and source. False when the caret is in none. */
|
|
142
141
|
toggle() {
|
|
143
142
|
this.settle();
|
|
144
|
-
|
|
145
|
-
if (!this.enabled || comment === undefined || !this.canRender(comment)) {
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
this.setRendered(comment, !this.wantsRendered(comment));
|
|
149
|
-
return true;
|
|
143
|
+
return this.switch(this.at(this.language.editor.getCaret().line));
|
|
150
144
|
}
|
|
151
145
|
/* Switches the comment that starts on a line, which is what its gutter button does. */
|
|
152
146
|
toggleAt(startLine) {
|
|
153
147
|
this.settle();
|
|
154
|
-
|
|
148
|
+
return this.switch(this.comments.find(candidate => candidate.startLine === startLine));
|
|
149
|
+
}
|
|
150
|
+
switch(comment) {
|
|
155
151
|
if (!this.enabled || comment === undefined || !this.canRender(comment)) {
|
|
156
152
|
return false;
|
|
157
153
|
}
|
|
@@ -267,16 +263,7 @@ export class RenderedDocsFeature {
|
|
|
267
263
|
if (line < 0) {
|
|
268
264
|
return;
|
|
269
265
|
}
|
|
270
|
-
const text = editor
|
|
271
|
-
start: {
|
|
272
|
-
line,
|
|
273
|
-
character: 0
|
|
274
|
-
},
|
|
275
|
-
end: {
|
|
276
|
-
line,
|
|
277
|
-
character: Number.MAX_SAFE_INTEGER
|
|
278
|
-
}
|
|
279
|
-
});
|
|
266
|
+
const text = lineTextOf(editor, line);
|
|
280
267
|
const character = line > comment.endLine ? /^[ \t]*/.exec(text)?.[0].length ?? 0 : text.length;
|
|
281
268
|
const place = {
|
|
282
269
|
line,
|
|
@@ -344,7 +331,7 @@ export class RenderedDocsFeature {
|
|
|
344
331
|
this.bump();
|
|
345
332
|
}
|
|
346
333
|
parse(comment) {
|
|
347
|
-
const key =
|
|
334
|
+
const key = comment.body;
|
|
348
335
|
let result = this.parsed.get(key);
|
|
349
336
|
if (result === undefined) {
|
|
350
337
|
if (this.parsed.size >= MAX_PARSED) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Editor, EditorContentChange, EditorRange } from '@adecore/editor';
|
|
2
2
|
import type { SnippetTransform } from './snippet.ts';
|
|
3
3
|
import type { SnippetTransformLease } from './snippet-transform-queue.ts';
|
|
4
|
+
import type { SnippetTransformJob } from './snippet-transform-protocol.ts';
|
|
4
5
|
export interface FieldTransformTarget {
|
|
5
6
|
readonly id: symbol;
|
|
6
7
|
range: EditorRange;
|
|
@@ -12,6 +13,10 @@ export interface FieldTransformTarget {
|
|
|
12
13
|
readonly order: readonly number[];
|
|
13
14
|
readonly ancestors?: readonly symbol[];
|
|
14
15
|
}
|
|
16
|
+
export declare function jobTable(): {
|
|
17
|
+
readonly jobs: SnippetTransformJob[];
|
|
18
|
+
readonly slotOf: (job: SnippetTransformJob) => number;
|
|
19
|
+
};
|
|
15
20
|
export declare class SnippetFieldTransforms {
|
|
16
21
|
private readonly entries;
|
|
17
22
|
private applying;
|
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
import { comparePositions, shiftPosition } from './diagnostics-model.js';
|
|
2
|
+
/* Each distinct job once, so a transform that several ranges share is computed once; `slotOf` is its index in `jobs`. */
|
|
3
|
+
export function jobTable() {
|
|
4
|
+
const jobs = [];
|
|
5
|
+
const keys = new Map();
|
|
6
|
+
const slotOf = job => {
|
|
7
|
+
const key = JSON.stringify(job);
|
|
8
|
+
let slot = keys.get(key);
|
|
9
|
+
if (slot === undefined) {
|
|
10
|
+
slot = jobs.length;
|
|
11
|
+
keys.set(key, slot);
|
|
12
|
+
jobs.push(job);
|
|
13
|
+
}
|
|
14
|
+
return slot;
|
|
15
|
+
};
|
|
16
|
+
return {
|
|
17
|
+
jobs,
|
|
18
|
+
slotOf
|
|
19
|
+
};
|
|
20
|
+
}
|
|
2
21
|
/* Navigation frames can pop before a worker replies; their tracked targets live independently. */
|
|
3
22
|
export class SnippetFieldTransforms {
|
|
4
23
|
entries = new Set();
|
|
@@ -36,22 +55,14 @@ export class SnippetFieldTransforms {
|
|
|
36
55
|
cancelled: false
|
|
37
56
|
};
|
|
38
57
|
this.entries.add(entry);
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
let slot = keys.get(key);
|
|
48
|
-
if (slot === undefined) {
|
|
49
|
-
slot = jobs.length;
|
|
50
|
-
keys.set(key, slot);
|
|
51
|
-
jobs.push(job);
|
|
52
|
-
}
|
|
53
|
-
return slot;
|
|
54
|
-
});
|
|
58
|
+
const {
|
|
59
|
+
jobs,
|
|
60
|
+
slotOf
|
|
61
|
+
} = jobTable();
|
|
62
|
+
const slots = targets.map(target => slotOf({
|
|
63
|
+
...target.transform,
|
|
64
|
+
value: target.value
|
|
65
|
+
}));
|
|
55
66
|
const valid = () => !entry.cancelled && entry.current() && targets.every(target => this.editor.textInRange(target.range) === target.value);
|
|
56
67
|
void this.lease.request(jobs, {
|
|
57
68
|
kind: 'exit',
|
package/dist/snippet-session.js
CHANGED
|
@@ -5,16 +5,10 @@ import { acquireSnippetTransforms } from './snippet-transform-queue.js';
|
|
|
5
5
|
import { realTimers } from './timers.js';
|
|
6
6
|
import { KEY_PRIORITY } from './key-priority.js';
|
|
7
7
|
import { MAX_TRANSFORM_CHARACTERS } from './snippet-transform-protocol.js';
|
|
8
|
-
import { comparePositions, rangeHolds, shiftPosition } from './diagnostics-model.js';
|
|
8
|
+
import { comparePositions, endOfInsertion, rangeHolds, sameRange, shiftPosition } from './diagnostics-model.js';
|
|
9
|
+
import { textBeforeOf } from './line-text.js';
|
|
9
10
|
export function positionInInsertion(start, text, offset) {
|
|
10
|
-
|
|
11
|
-
return lines.length === 1 ? {
|
|
12
|
-
line: start.line,
|
|
13
|
-
character: start.character + offset
|
|
14
|
-
} : {
|
|
15
|
-
line: start.line + lines.length - 1,
|
|
16
|
-
character: lines[lines.length - 1].length
|
|
17
|
-
};
|
|
11
|
+
return endOfInsertion(start, text.slice(0, offset));
|
|
18
12
|
}
|
|
19
13
|
function mapRange(range, changes, grow) {
|
|
20
14
|
let {
|
|
@@ -198,7 +192,7 @@ export class SnippetFeature {
|
|
|
198
192
|
off();
|
|
199
193
|
}
|
|
200
194
|
const now = this.language.editor.getSelections();
|
|
201
|
-
return valid && current() && now.length === selections.length && now.every((range, index) =>
|
|
195
|
+
return valid && current() && now.length === selections.length && now.every((range, index) => sameRange(range, selections[index]));
|
|
202
196
|
}
|
|
203
197
|
get isActive() {
|
|
204
198
|
return this.frames.length > 0;
|
|
@@ -489,13 +483,7 @@ export class SnippetFeature {
|
|
|
489
483
|
const groups = [];
|
|
490
484
|
let characters = 0;
|
|
491
485
|
for (const [index, target] of targets.entries()) {
|
|
492
|
-
const before = editor.
|
|
493
|
-
start: {
|
|
494
|
-
line: target.range.start.line,
|
|
495
|
-
character: 0
|
|
496
|
-
},
|
|
497
|
-
end: target.range.start
|
|
498
|
-
});
|
|
486
|
+
const before = textBeforeOf(editor, target.range.start);
|
|
499
487
|
const prefix = /^[\t ]*/.exec(before)?.[0] ?? '';
|
|
500
488
|
const value = values[index];
|
|
501
489
|
let newlines = 0;
|
|
@@ -586,7 +574,7 @@ export class SnippetFeature {
|
|
|
586
574
|
const captured = [...this.placed, ...active, ...trackedFrames.flatMap(frame => frame.instances.flatMap(instance => instance.fields.flatMap(field => field.ranges.filter(range => !transformIds.has(range.id) || comparePositions(range.start, range.end) < 0))))];
|
|
587
575
|
const ancestorsOf = new Map(targets.map(target => [target, new Set([...(target.ancestors ?? []), ...target.frame.instances.flatMap(instance => instance.fields.flatMap(field => target.owner === instance && target.parents?.includes(field.index) === true ? field.ranges.filter(range => rangeHolds(range, target.range.start) && rangeHolds(range, target.range.end)).map(range => range.id) : []))])]));
|
|
588
576
|
const map = (range, parent) => {
|
|
589
|
-
const tracked = 'order' in range ? range : captured.find(candidate =>
|
|
577
|
+
const tracked = 'order' in range ? range : captured.find(candidate => sameRange(candidate, range));
|
|
590
578
|
let {
|
|
591
579
|
start,
|
|
592
580
|
end
|
|
@@ -665,7 +653,7 @@ export class SnippetFeature {
|
|
|
665
653
|
for (const field of instance.fields) {
|
|
666
654
|
const primary = field.ranges[field.primary];
|
|
667
655
|
field.ranges = field.ranges.filter(range => !range.parents?.includes(index) || !owned.some(edit => comparePositions(edit.range.start, edit.range.end) < 0 && (comparePositions(range.start, range.end) === 0 ? rangeHolds(edit.range, range.start) : comparePositions(edit.range.start, range.end) < 0 && comparePositions(range.start, edit.range.end) < 0)));
|
|
668
|
-
field.primary = Math.max(0, field.ranges.
|
|
656
|
+
field.primary = Math.max(0, field.ranges.indexOf(primary));
|
|
669
657
|
field.ranges = field.ranges.map(range => {
|
|
670
658
|
const replacement = this.detached?.ranges.get(range.id);
|
|
671
659
|
if (replacement !== undefined) {
|
|
@@ -712,7 +700,7 @@ export class SnippetFeature {
|
|
|
712
700
|
if (!this.isActive) {
|
|
713
701
|
return;
|
|
714
702
|
}
|
|
715
|
-
const extras = new Set(change.changes.filter(change => this.extras.some(extra =>
|
|
703
|
+
const extras = new Set(change.changes.filter(change => this.extras.some(extra => sameRange(extra.range, change.range) && extra.text === change.text)));
|
|
716
704
|
for (const frame of this.frames) {
|
|
717
705
|
if (!this.mapFrame(frame, change.changes, extras)) {
|
|
718
706
|
const caret = this.language.editor.getCaret();
|