@adecore/editor-react 0.22.1 → 0.23.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.d.ts +6 -0
- package/dist/CallHierarchyPanel.js +92 -0
- package/dist/CompletionPopup.js +11 -3
- package/dist/DiffView.d.ts +22 -0
- package/dist/DiffView.js +203 -0
- package/dist/EditorContextMenu.js +1 -0
- package/dist/EditorView.js +18 -1
- package/dist/FindReplace.d.ts +1 -1
- package/dist/FindReplace.js +41 -11
- package/dist/HoverCard.js +2 -2
- package/dist/LanguagePopups.js +4 -1
- package/dist/Markdown.d.ts +2 -1
- package/dist/Markdown.js +8 -4
- package/dist/PanelResizeHandle.d.ts +8 -0
- package/dist/PanelResizeHandle.js +71 -0
- package/dist/PeekPanel.d.ts +3 -0
- package/dist/PeekPanel.js +7 -4
- package/dist/PickPopup.js +15 -2
- package/dist/RenameCard.js +10 -5
- package/dist/RenderedDocs.d.ts +9 -0
- package/dist/RenderedDocs.js +84 -0
- package/dist/SplitDiffView.d.ts +9 -0
- package/dist/SplitDiffView.js +276 -0
- package/dist/SymbolPicker.d.ts +3 -0
- package/dist/SymbolPicker.js +4 -3
- package/dist/call-hierarchy-model.d.ts +52 -0
- package/dist/call-hierarchy-model.js +93 -0
- package/dist/call-hierarchy.d.ts +42 -0
- package/dist/call-hierarchy.js +398 -0
- package/dist/code-actions-model.d.ts +2 -0
- package/dist/code-actions-model.js +12 -10
- package/dist/code-actions.js +3 -1
- package/dist/completion-model.d.ts +14 -4
- package/dist/completion-model.js +40 -33
- package/dist/completion.d.ts +4 -0
- package/dist/completion.js +257 -42
- package/dist/diagnostics.js +8 -6
- package/dist/diff-expansion.d.ts +9 -0
- package/dist/diff-expansion.js +41 -0
- package/dist/diff-overlays.d.ts +15 -0
- package/dist/diff-overlays.js +28 -0
- package/dist/diff-syntax.d.ts +16 -0
- package/dist/diff-syntax.js +49 -0
- package/dist/diff-wordmarks.d.ts +5 -0
- package/dist/diff-wordmarks.js +26 -0
- package/dist/doc-comment-model.d.ts +23 -0
- package/dist/doc-comment-model.js +291 -0
- package/dist/editor-language.d.ts +13 -1
- package/dist/editor-language.js +15 -5
- package/dist/editor-react.css +79 -0
- package/dist/format-on-type.d.ts +14 -0
- package/dist/format-on-type.js +96 -0
- package/dist/host-types.d.ts +19 -0
- package/dist/hover.d.ts +4 -0
- package/dist/hover.js +15 -2
- package/dist/index.d.ts +19 -5
- package/dist/index.js +9 -1
- package/dist/inlay-hints.d.ts +47 -2
- package/dist/inlay-hints.js +353 -16
- package/dist/language-ids.js +4 -2
- package/dist/locales/en.json +117 -3
- package/dist/locales/nl.json +117 -3
- package/dist/models.d.ts +6 -2
- package/dist/models.js +5 -1
- package/dist/panel-size.d.ts +6 -0
- package/dist/panel-size.js +25 -0
- package/dist/patch-decorations.d.ts +27 -0
- package/dist/patch-decorations.js +182 -0
- package/dist/patch-layout.d.ts +40 -0
- package/dist/patch-layout.js +304 -0
- package/dist/peek.d.ts +3 -1
- package/dist/peek.js +6 -1
- package/dist/pick.d.ts +1 -0
- package/dist/pick.js +11 -1
- package/dist/popups.d.ts +17 -0
- package/dist/popups.js +1 -0
- package/dist/project-language.d.ts +12 -0
- package/dist/project-language.js +52 -2
- package/dist/refresher.d.ts +1 -0
- package/dist/refresher.js +5 -0
- package/dist/rename-model.d.ts +1 -1
- package/dist/rename-model.js +3 -7
- package/dist/rename.js +5 -8
- package/dist/rendered-docs.d.ts +64 -0
- package/dist/rendered-docs.js +374 -0
- package/dist/row-host.d.ts +2 -0
- package/dist/row-host.js +11 -3
- package/dist/semantic-delta.d.ts +2 -0
- package/dist/semantic-delta.js +27 -0
- package/dist/semantic-model.js +16 -0
- package/dist/semantic-tokens.d.ts +26 -1
- package/dist/semantic-tokens.js +174 -14
- package/dist/shortcut-keys.d.ts +2 -0
- package/dist/shortcut-keys.js +6 -2
- package/dist/signature.js +1 -1
- package/dist/snippet-field-transforms.d.ts +30 -0
- package/dist/snippet-field-transforms.js +116 -0
- package/dist/snippet-indent.d.ts +2 -0
- package/dist/snippet-indent.js +49 -0
- package/dist/snippet-range-order.d.ts +3 -0
- package/dist/snippet-range-order.js +19 -0
- package/dist/snippet-session.d.ts +42 -6
- package/dist/snippet-session.js +650 -53
- package/dist/snippet-transform-client.d.ts +19 -0
- package/dist/snippet-transform-client.js +66 -0
- package/dist/snippet-transform-format.d.ts +15 -0
- package/dist/snippet-transform-format.js +302 -0
- package/dist/snippet-transform-protocol.d.ts +45 -0
- package/dist/snippet-transform-protocol.js +8 -0
- package/dist/snippet-transform-queue.d.ts +32 -0
- package/dist/snippet-transform-queue.js +173 -0
- package/dist/snippet-transform-worker.d.ts +10 -0
- package/dist/snippet-transform-worker.js +41 -0
- package/dist/snippet-variable-names.d.ts +1 -0
- package/dist/snippet-variable-names.js +36 -0
- package/dist/snippet-variables.d.ts +7 -0
- package/dist/snippet-variables.js +66 -0
- package/dist/snippet.d.ts +30 -3
- package/dist/snippet.js +402 -87
- package/dist/split-alignment.d.ts +6 -0
- package/dist/split-alignment.js +38 -0
- package/dist/split-scroll.d.ts +14 -0
- package/dist/split-scroll.js +45 -0
- package/dist/symbols.d.ts +2 -1
- package/dist/symbols.js +39 -7
- package/dist/testing.d.ts +5 -1
- package/dist/testing.js +12 -0
- package/dist/timers.d.ts +1 -0
- package/dist/timers.js +4 -0
- package/dist/use-panel-body.d.ts +6 -0
- package/dist/use-panel-body.js +13 -0
- package/dist/workspace-edit.d.ts +25 -3
- package/dist/workspace-edit.js +282 -143
- package/package.json +10 -5
- package/src/CallHierarchyPanel.tsx +244 -0
- package/src/CompletionPopup.tsx +12 -1
- package/src/DiffView.tsx +262 -0
- package/src/EditorContextMenu.tsx +1 -0
- package/src/EditorView.tsx +21 -2
- package/src/FindReplace.tsx +51 -13
- package/src/HoverCard.tsx +23 -21
- package/src/LanguagePopups.tsx +5 -0
- package/src/Markdown.tsx +10 -3
- package/src/PanelResizeHandle.tsx +104 -0
- package/src/PeekPanel.tsx +11 -6
- package/src/PickPopup.tsx +18 -2
- package/src/RenameCard.tsx +18 -0
- package/src/RenderedDocs.tsx +155 -0
- package/src/SplitDiffView.tsx +337 -0
- package/src/SymbolPicker.tsx +4 -3
- package/src/call-hierarchy-model.ts +151 -0
- package/src/call-hierarchy.ts +440 -0
- package/src/code-actions-model.ts +17 -11
- package/src/code-actions.ts +3 -1
- package/src/completion-model.ts +61 -35
- package/src/completion.ts +302 -42
- package/src/diagnostics.ts +8 -6
- package/src/diff-expansion.ts +49 -0
- package/src/diff-overlays.tsx +69 -0
- package/src/diff-syntax.ts +67 -0
- package/src/diff-wordmarks.ts +31 -0
- package/src/doc-comment-model.ts +336 -0
- package/src/editor-language.ts +22 -6
- package/src/editor-react.css +79 -0
- package/src/format-on-type.ts +111 -0
- package/src/host-types.ts +23 -0
- package/src/hover.ts +18 -3
- package/src/index.ts +27 -4
- package/src/inlay-hints.ts +394 -18
- package/src/language-ids.ts +4 -2
- package/src/locales/en.json +117 -3
- package/src/locales/nl.json +117 -3
- package/src/models.ts +40 -1
- package/src/panel-size.ts +28 -0
- package/src/patch-decorations.ts +224 -0
- package/src/patch-layout.ts +388 -0
- package/src/peek.ts +6 -1
- package/src/pick.ts +12 -1
- package/src/popups.ts +24 -0
- package/src/project-language.ts +63 -2
- package/src/refresher.ts +6 -0
- package/src/rename-model.ts +4 -8
- package/src/rename.ts +5 -8
- package/src/rendered-docs.ts +430 -0
- package/src/row-host.ts +12 -3
- package/src/semantic-delta.ts +29 -0
- package/src/semantic-model.ts +17 -0
- package/src/semantic-tokens.ts +202 -19
- package/src/shortcut-keys.ts +6 -2
- package/src/signature.ts +1 -1
- package/src/snippet-field-transforms.ts +144 -0
- package/src/snippet-indent.ts +53 -0
- package/src/snippet-range-order.ts +19 -0
- package/src/snippet-session.ts +797 -56
- package/src/snippet-transform-client.ts +84 -0
- package/src/snippet-transform-format.ts +325 -0
- package/src/snippet-transform-protocol.ts +24 -0
- package/src/snippet-transform-queue.ts +199 -0
- package/src/snippet-transform-worker.ts +58 -0
- package/src/snippet-variable-names.ts +36 -0
- package/src/snippet-variables.ts +74 -0
- package/src/snippet.ts +452 -89
- package/src/split-alignment.ts +45 -0
- package/src/split-scroll.ts +54 -0
- package/src/symbols.ts +43 -7
- package/src/testing.ts +25 -0
- package/src/timers.ts +5 -0
- package/src/use-panel-body.ts +17 -0
- package/src/workspace-edit.ts +347 -169
package/dist/inlay-hints.js
CHANGED
|
@@ -1,20 +1,41 @@
|
|
|
1
|
+
import { hoverTextOf, isEmptyHover } from './hover-content.js';
|
|
2
|
+
import { SHOW_DELAY_MS } from './hover.js';
|
|
3
|
+
import { messageOf } from './error-message.js';
|
|
1
4
|
import { lineTextOf } from './line-text.js';
|
|
2
5
|
import { Refresher } from './refresher.js';
|
|
3
6
|
import { realTimers } from './timers.js';
|
|
4
7
|
const METHOD = 'textDocument/inlayHint';
|
|
8
|
+
const RESOLVE_METHOD = 'inlayHint/resolve';
|
|
9
|
+
const TOAST_ID = 'language-inlay-hint';
|
|
5
10
|
const PAUSE_MS = 300;
|
|
11
|
+
/* How long a click on a hint that a double click would also act on waits for its second press; the editor's own interval for a double press. */
|
|
12
|
+
const DOUBLE_PRESS_MS = 500;
|
|
6
13
|
/* Lines asked for above and below what is in view, so a little scrolling stays inside what is already drawn. */
|
|
7
14
|
const MARGIN_LINES = 60;
|
|
8
15
|
/* A hint's label is a string or a list of parts, which are read as one text. */
|
|
9
16
|
export function hintLabelOf(hint) {
|
|
10
17
|
return typeof hint.label === 'string' ? hint.label : hint.label.map((part) => part.value).join('');
|
|
11
18
|
}
|
|
12
|
-
|
|
19
|
+
/* The hints worth drawing, in the order of the text. */
|
|
20
|
+
export function drawableHints(hints) {
|
|
13
21
|
return hints
|
|
14
|
-
.
|
|
15
|
-
.filter((hint) => hint.label !== '')
|
|
22
|
+
.filter((hint) => hintLabelOf(hint) !== '')
|
|
16
23
|
.sort((left, right) => left.position.line - right.position.line || left.position.character - right.position.character);
|
|
17
24
|
}
|
|
25
|
+
/* A hint as the editor draws it: a label with parts keeps them apart so each can be pointed at. */
|
|
26
|
+
export function editorHintOf(hint, id) {
|
|
27
|
+
return {
|
|
28
|
+
id,
|
|
29
|
+
position: hint.position,
|
|
30
|
+
label: hintLabelOf(hint),
|
|
31
|
+
...(typeof hint.label === 'string' ? {} : { parts: hint.label.map((part) => part.value) }),
|
|
32
|
+
...(hint.paddingLeft ? { paddingLeft: true } : {}),
|
|
33
|
+
...(hint.paddingRight ? { paddingRight: true } : {})
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export function editorHintsOf(hints) {
|
|
37
|
+
return drawableHints(hints).map((hint, index) => editorHintOf(hint, `hint-${index}`));
|
|
38
|
+
}
|
|
18
39
|
/* The lines in view with a margin of lines around them, from the start of the first to the end of the last. */
|
|
19
40
|
export function withMargin(visible, endOfDocument, endOfLine) {
|
|
20
41
|
const first = Math.max(0, visible.start.line - MARGIN_LINES);
|
|
@@ -25,16 +46,102 @@ export function withMargin(visible, endOfDocument, endOfLine) {
|
|
|
25
46
|
export function covers(asked, visible) {
|
|
26
47
|
return asked.start.line <= visible.start.line && asked.end.line >= visible.end.line;
|
|
27
48
|
}
|
|
49
|
+
/* The words of a tooltip: plain text is escaped so it reads as typed, Markdown goes through the hover card's own rendering. */
|
|
50
|
+
export function tooltipTextOf(tooltip) {
|
|
51
|
+
if (tooltip === undefined) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const text = hoverTextOf({ contents: typeof tooltip === 'string' ? { kind: 'plaintext', value: tooltip } : tooltip });
|
|
55
|
+
return isEmptyHover(text) ? null : text;
|
|
56
|
+
}
|
|
57
|
+
/* One part of a hint's label, or null for a hint whose label is a string. */
|
|
58
|
+
export function partOf(hint, index) {
|
|
59
|
+
return index === null || typeof hint.label === 'string' ? null : (hint.label[index] ?? null);
|
|
60
|
+
}
|
|
61
|
+
/* What a hover shows: the part's tooltip, else the hint's, else (with no part to point at) that of the first part that has one. */
|
|
62
|
+
export function tooltipOf(hint, index) {
|
|
63
|
+
const part = partOf(hint, index);
|
|
64
|
+
const own = part?.tooltip ?? hint.tooltip;
|
|
65
|
+
if (own !== undefined || index !== null || typeof hint.label === 'string') {
|
|
66
|
+
return tooltipTextOf(own);
|
|
67
|
+
}
|
|
68
|
+
return tooltipTextOf(hint.label.find((candidate) => candidate.tooltip !== undefined)?.tooltip);
|
|
69
|
+
}
|
|
70
|
+
/* Whether a server could still say more about the hint: nothing in it gives a tooltip, a place or a command yet. */
|
|
71
|
+
export function lacksInfo(hint, index) {
|
|
72
|
+
if (hint.tooltip !== undefined) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const parts = typeof hint.label === 'string' ? [] : index === null ? hint.label : [hint.label[index]];
|
|
76
|
+
return !parts.some((part) => part !== undefined && (part.tooltip !== undefined || part.location !== undefined || part.command !== undefined));
|
|
77
|
+
}
|
|
78
|
+
/* Whether the edits fit the text as it is now: positions inside it, each range in order, none overlapping the next. */
|
|
79
|
+
export function editsFit(editor, edits) {
|
|
80
|
+
const end = editor.positionAt(Number.MAX_SAFE_INTEGER);
|
|
81
|
+
const inside = (position) => Number.isInteger(position.line) &&
|
|
82
|
+
Number.isInteger(position.character) &&
|
|
83
|
+
position.line >= 0 &&
|
|
84
|
+
position.character >= 0 &&
|
|
85
|
+
position.line <= end.line &&
|
|
86
|
+
position.character <= lineTextOf(editor, position.line).length;
|
|
87
|
+
const before = (left, right) => left.line < right.line || (left.line === right.line && left.character <= right.character);
|
|
88
|
+
const ordered = [...edits].sort((left, right) => (before(left.range.start, right.range.start) ? -1 : 1));
|
|
89
|
+
return ordered.every((edit, index) => {
|
|
90
|
+
const next = ordered[index + 1];
|
|
91
|
+
return (inside(edit.range.start) &&
|
|
92
|
+
inside(edit.range.end) &&
|
|
93
|
+
before(edit.range.start, edit.range.end) &&
|
|
94
|
+
(next === undefined || before(edit.range.end, next.range.start)));
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/* What a press does, from what is known of the hint: a double click inserts it and nothing else, Mod+click follows a part's place, a plain click runs its command. */
|
|
98
|
+
export function actionOf(hint, click) {
|
|
99
|
+
if (click.clicks === 2) {
|
|
100
|
+
return hint.textEdits !== undefined && hint.textEdits.length > 0 ? { kind: 'insert', edits: hint.textEdits } : null;
|
|
101
|
+
}
|
|
102
|
+
const part = partOf(hint, click.part);
|
|
103
|
+
if (click.mod) {
|
|
104
|
+
return part?.location === undefined ? null : { kind: 'go', location: part.location };
|
|
105
|
+
}
|
|
106
|
+
return part?.command === undefined ? null : { kind: 'run', command: part.command };
|
|
107
|
+
}
|
|
28
108
|
/*
|
|
29
109
|
* Types and parameter names the servers infer, asked for the lines in view and a margin around them. The
|
|
30
110
|
* last ones stay until the answer comes, so nothing blinks while typing; scrolling asks again only once the
|
|
31
111
|
* view leaves the lines that were asked for.
|
|
112
|
+
*
|
|
113
|
+
* A hint can be acted on with the pointer. The server is asked to resolve a hint as the pointer arrives, and
|
|
114
|
+
* resting on it for the hover delay shows its tooltip. Mod+click on a part follows its place, a click on a
|
|
115
|
+
* part runs its command and a double click inserts its text edits as one undo step, never together with the
|
|
116
|
+
* command: when a hint has both, the command waits for the double press interval. A press on a hint that
|
|
117
|
+
* offers nothing (yet) moves the caret as beside the text. A finger has no hover, so a tap shows the
|
|
118
|
+
* tooltip and does nothing more. All of it is for the pointer: the hints are not part
|
|
119
|
+
* of the text and stay out of the keyboard's way, and Quick Info on the caret shows the tooltip of a hint that
|
|
120
|
+
* sits there.
|
|
32
121
|
*/
|
|
33
122
|
export class InlayHintsFeature {
|
|
123
|
+
language;
|
|
124
|
+
timers;
|
|
34
125
|
refresher;
|
|
35
126
|
/* The lines the hints on screen were asked for. */
|
|
36
127
|
asked = null;
|
|
128
|
+
entries = new Map();
|
|
129
|
+
shown = [];
|
|
130
|
+
generation = 0;
|
|
131
|
+
/* Counts the edits of the text, which hints and answers made for an earlier one are tested against. */
|
|
132
|
+
epoch = 0;
|
|
133
|
+
target = null;
|
|
134
|
+
modHeld = false;
|
|
135
|
+
showTimer;
|
|
136
|
+
/* The command of a click that may still become a double click. */
|
|
137
|
+
commandTimer;
|
|
138
|
+
/* Counts presses, so one that waited for a resolve gives way to a later one. */
|
|
139
|
+
pressCount = 0;
|
|
140
|
+
/* Counts what the pointer does, so a tooltip that was waited for is dropped once the pointer has moved on. */
|
|
141
|
+
request = 0;
|
|
37
142
|
constructor(language, timers = realTimers) {
|
|
143
|
+
this.language = language;
|
|
144
|
+
this.timers = timers;
|
|
38
145
|
const { editor, project, uri } = language;
|
|
39
146
|
this.refresher = new Refresher(async (signal) => {
|
|
40
147
|
const { service } = project;
|
|
@@ -46,26 +153,256 @@ export class InlayHintsFeature {
|
|
|
46
153
|
character: lineTextOf(editor, line).length
|
|
47
154
|
}));
|
|
48
155
|
this.asked = range;
|
|
156
|
+
const epoch = this.epoch;
|
|
49
157
|
const hints = await service.inlayHints(uri, range, { signal });
|
|
50
|
-
if (!signal.aborted) {
|
|
51
|
-
|
|
158
|
+
if (!signal.aborted && epoch === this.epoch) {
|
|
159
|
+
this.install(hints === null ? [] : drawableHints(hints), epoch);
|
|
52
160
|
}
|
|
53
161
|
}, PAUSE_MS, timers);
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
162
|
+
const offs = [
|
|
163
|
+
editor.onTextChange(() => {
|
|
164
|
+
this.epoch++;
|
|
165
|
+
this.cancelHover();
|
|
57
166
|
this.refresher.later();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
167
|
+
}),
|
|
168
|
+
editor.onViewChange(() => {
|
|
169
|
+
if (this.asked !== null && !covers(this.asked, editor.getVisibleRange())) {
|
|
170
|
+
this.refresher.later();
|
|
171
|
+
}
|
|
172
|
+
}),
|
|
173
|
+
language.onProvidersChanged(() => {
|
|
174
|
+
this.refresher.now();
|
|
175
|
+
})
|
|
176
|
+
];
|
|
177
|
+
if (editor.onInlayHover !== undefined) {
|
|
178
|
+
offs.push(editor.onInlayHover((target) => this.hovered(target === null ? null : { id: target.id, part: target.part })));
|
|
179
|
+
}
|
|
180
|
+
if (editor.onInlayClick !== undefined) {
|
|
181
|
+
offs.push(editor.onInlayClick((click) => this.pressed(click)));
|
|
182
|
+
}
|
|
183
|
+
if (typeof window !== 'undefined') {
|
|
184
|
+
const modKey = language.apple ? 'Meta' : 'Control';
|
|
185
|
+
const keys = (event) => {
|
|
186
|
+
if (event.key === modKey) {
|
|
187
|
+
this.setModHeld(event.type === 'keydown');
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
const lost = () => this.setModHeld(false);
|
|
191
|
+
window.addEventListener('keydown', keys);
|
|
192
|
+
window.addEventListener('keyup', keys);
|
|
193
|
+
window.addEventListener('blur', lost);
|
|
194
|
+
offs.push(() => {
|
|
195
|
+
window.removeEventListener('keydown', keys);
|
|
196
|
+
window.removeEventListener('keyup', keys);
|
|
197
|
+
window.removeEventListener('blur', lost);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
63
200
|
language.onDispose(() => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
201
|
+
for (const off of offs) {
|
|
202
|
+
off();
|
|
203
|
+
}
|
|
204
|
+
this.cancelHover();
|
|
205
|
+
this.timers.clear(this.commandTimer);
|
|
67
206
|
this.refresher.dispose();
|
|
207
|
+
this.entries.clear();
|
|
68
208
|
editor.setInlayHints([]);
|
|
69
209
|
});
|
|
70
210
|
}
|
|
211
|
+
/* The key that follows a place is down or up; it comes from the window, as the editor only says where the pointer is. */
|
|
212
|
+
setModHeld(held) {
|
|
213
|
+
if (held !== this.modHeld) {
|
|
214
|
+
this.modHeld = held;
|
|
215
|
+
this.updateLink();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/* 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. */
|
|
219
|
+
async showTooltipAt(position) {
|
|
220
|
+
const found = [...this.entries].find(([, entry]) => entry.epoch === this.epoch && sameSpot(entry.hint.position, position));
|
|
221
|
+
if (found === undefined) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
return this.showTooltip({ id: found[0], part: null }, this.request, false);
|
|
225
|
+
}
|
|
226
|
+
install(hints, epoch) {
|
|
227
|
+
this.generation++;
|
|
228
|
+
this.entries.clear();
|
|
229
|
+
this.shown = hints.map((hint, index) => {
|
|
230
|
+
const id = `hint-${this.generation}-${index}`;
|
|
231
|
+
this.entries.set(id, { id, hint, epoch, resolution: null, answered: false });
|
|
232
|
+
return editorHintOf(hint, id);
|
|
233
|
+
});
|
|
234
|
+
this.language.editor.setInlayHints(this.shown);
|
|
235
|
+
this.target = null;
|
|
236
|
+
this.updateLink();
|
|
237
|
+
}
|
|
238
|
+
hovered(target) {
|
|
239
|
+
this.target = target;
|
|
240
|
+
this.timers.clear(this.showTimer);
|
|
241
|
+
const request = ++this.request;
|
|
242
|
+
this.updateLink();
|
|
243
|
+
if (target === null) {
|
|
244
|
+
if (this.cardOpen()) {
|
|
245
|
+
this.language.hover.release();
|
|
246
|
+
}
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
this.showTimer = this.timers.set(() => void this.showTooltip(target, request), SHOW_DELAY_MS);
|
|
250
|
+
// A press has to know what the hint offers when it comes, so the answer is asked for as the pointer arrives.
|
|
251
|
+
const entry = this.entries.get(target.id);
|
|
252
|
+
if (entry !== undefined && this.resolvable(entry)) {
|
|
253
|
+
void this.resolve(entry).then(() => this.updateLink());
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
cancelHover() {
|
|
257
|
+
this.timers.clear(this.showTimer);
|
|
258
|
+
this.request++;
|
|
259
|
+
this.target = null;
|
|
260
|
+
this.updateLink();
|
|
261
|
+
if (this.cardOpen()) {
|
|
262
|
+
this.language.hover.hide();
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/* Whether the card up is a tooltip, which is the only kind that has no footer. */
|
|
266
|
+
cardOpen() {
|
|
267
|
+
return this.language.popups.getState().hover?.info?.plain === true;
|
|
268
|
+
}
|
|
269
|
+
/* The card for a hint or part, once the server has said what it can. Says whether it opened. */
|
|
270
|
+
async showTooltip(target, request, pointer = true) {
|
|
271
|
+
const entry = this.entries.get(target.id);
|
|
272
|
+
if (entry === undefined) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
if (lacksInfo(entry.hint, target.part)) {
|
|
276
|
+
await this.resolve(entry);
|
|
277
|
+
this.updateLink();
|
|
278
|
+
}
|
|
279
|
+
if ((pointer && request !== this.request) || entry.epoch !== this.epoch) {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
const text = tooltipOf(entry.hint, target.part);
|
|
283
|
+
if (text === null) {
|
|
284
|
+
if (this.cardOpen()) {
|
|
285
|
+
this.language.hover.hide();
|
|
286
|
+
}
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
this.language.hover.showText(entry.hint.position, text);
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
/* Asks the server for more of a hint, once; what it says is kept only while the text is the one the hint was made for. */
|
|
293
|
+
resolve(entry) {
|
|
294
|
+
if (entry.resolution !== null) {
|
|
295
|
+
return entry.resolution;
|
|
296
|
+
}
|
|
297
|
+
const { service } = this.language.project;
|
|
298
|
+
if (entry.epoch !== this.epoch || !service.supports(RESOLVE_METHOD, this.language.uri)) {
|
|
299
|
+
return Promise.resolve(entry.hint);
|
|
300
|
+
}
|
|
301
|
+
entry.resolution = service
|
|
302
|
+
.resolveInlayHint(this.language.uri, entry.hint)
|
|
303
|
+
.then((resolved) => {
|
|
304
|
+
if (entry.epoch === this.epoch) {
|
|
305
|
+
entry.hint = { ...entry.hint, ...resolved, position: entry.hint.position };
|
|
306
|
+
}
|
|
307
|
+
return entry.hint;
|
|
308
|
+
})
|
|
309
|
+
.catch(() => entry.hint)
|
|
310
|
+
.finally(() => {
|
|
311
|
+
entry.answered = true;
|
|
312
|
+
});
|
|
313
|
+
return entry.resolution;
|
|
314
|
+
}
|
|
315
|
+
/* Whether asking the server could still change what the hint offers. */
|
|
316
|
+
resolvable(entry) {
|
|
317
|
+
return !entry.answered && entry.epoch === this.epoch && this.language.project.service.supports(RESOLVE_METHOD, this.language.uri);
|
|
318
|
+
}
|
|
319
|
+
/* Underlines the part under the pointer when a press would do something with it: a command always, a place while the key is down. */
|
|
320
|
+
updateLink() {
|
|
321
|
+
const { target } = this;
|
|
322
|
+
const entry = target === null ? undefined : this.entries.get(target.id);
|
|
323
|
+
const part = entry === undefined || target === null || entry.epoch !== this.epoch ? null : partOf(entry.hint, target.part);
|
|
324
|
+
const link = part !== null && (part.command !== undefined || (this.modHeld && part.location !== undefined));
|
|
325
|
+
this.language.editor.setInlayLink?.(link ? target : null);
|
|
326
|
+
}
|
|
327
|
+
pressed(click) {
|
|
328
|
+
const entry = this.entries.get(click.id);
|
|
329
|
+
if (entry === undefined || entry.epoch !== this.epoch) {
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
if (click.touch) {
|
|
333
|
+
this.timers.clear(this.showTimer);
|
|
334
|
+
const request = ++this.request;
|
|
335
|
+
const worth = tooltipOf(entry.hint, click.part) !== null || this.resolvable(entry);
|
|
336
|
+
if (worth) {
|
|
337
|
+
void this.showTooltip({ id: click.id, part: click.part }, request);
|
|
338
|
+
}
|
|
339
|
+
return worth;
|
|
340
|
+
}
|
|
341
|
+
if (click.alt || click.shift) {
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
this.timers.clear(this.commandTimer);
|
|
345
|
+
const press = ++this.pressCount;
|
|
346
|
+
if (this.resolvable(entry) && (click.clicks === 2 || actionOf(entry.hint, click) !== null)) {
|
|
347
|
+
// The server may still add edits, which would make this the first press of a double click.
|
|
348
|
+
void this.resolve(entry).then(() => {
|
|
349
|
+
if (press === this.pressCount) {
|
|
350
|
+
this.press(entry, click);
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
return true;
|
|
354
|
+
}
|
|
355
|
+
return this.press(entry, click);
|
|
356
|
+
}
|
|
357
|
+
press(entry, click) {
|
|
358
|
+
this.timers.clear(this.commandTimer);
|
|
359
|
+
const action = actionOf(entry.hint, click);
|
|
360
|
+
const insertable = entry.hint.textEdits !== undefined && entry.hint.textEdits.length > 0;
|
|
361
|
+
if (action?.kind === 'run' && insertable) {
|
|
362
|
+
// A double click inserts the edits, so the command of its first press waits to see whether a second one comes.
|
|
363
|
+
const epoch = this.epoch;
|
|
364
|
+
this.commandTimer = this.timers.set(() => {
|
|
365
|
+
if (epoch === this.epoch) {
|
|
366
|
+
void this.perform(entry, action);
|
|
367
|
+
}
|
|
368
|
+
}, DOUBLE_PRESS_MS);
|
|
369
|
+
return true;
|
|
370
|
+
}
|
|
371
|
+
if (action !== null) {
|
|
372
|
+
void this.perform(entry, action);
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
// The first press of a double click must not move the caret either, or the second would land beside the hint.
|
|
376
|
+
return click.clicks === 1 && !click.mod && insertable;
|
|
377
|
+
}
|
|
378
|
+
async perform(entry, action) {
|
|
379
|
+
const { editor, project, uri } = this.language;
|
|
380
|
+
try {
|
|
381
|
+
if (action.kind === 'go') {
|
|
382
|
+
this.cancelHover();
|
|
383
|
+
this.language.goTo(action.location);
|
|
384
|
+
}
|
|
385
|
+
else if (action.kind === 'run') {
|
|
386
|
+
await project.service.executeCommand(uri, action.command);
|
|
387
|
+
}
|
|
388
|
+
else if (entry.epoch === this.epoch && editsFit(editor, action.edits)) {
|
|
389
|
+
this.cancelHover();
|
|
390
|
+
const applied = editor.applyEdits(action.edits.map((edit) => ({ range: edit.range, text: edit.newText })));
|
|
391
|
+
if (applied) {
|
|
392
|
+
this.shown = this.shown.filter((hint) => hint.id !== entry.id);
|
|
393
|
+
editor.setInlayHints(this.shown);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
catch (error) {
|
|
398
|
+
this.language.project.host.notify?.({
|
|
399
|
+
id: TOAST_ID,
|
|
400
|
+
kind: 'error',
|
|
401
|
+
title: project.i18n.t('editor:language.inlay.failed', { message: messageOf(error) })
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
function sameSpot(left, right) {
|
|
407
|
+
return left.line === right.line && left.character === right.character;
|
|
71
408
|
}
|
package/dist/language-ids.js
CHANGED
|
@@ -16,7 +16,8 @@ const LSP_IDS = {
|
|
|
16
16
|
python: 'python',
|
|
17
17
|
shellscript: 'shellscript',
|
|
18
18
|
docker: 'dockerfile',
|
|
19
|
-
twig: 'twig'
|
|
19
|
+
twig: 'twig',
|
|
20
|
+
sql: 'sql'
|
|
20
21
|
};
|
|
21
22
|
/* The LSP language id of a file, or null when no language server here knows its language. */
|
|
22
23
|
export function lspLanguageIdOf(language) {
|
|
@@ -43,7 +44,8 @@ const PATH_IDS = {
|
|
|
43
44
|
scss: 'scss',
|
|
44
45
|
html: 'html',
|
|
45
46
|
md: 'markdown',
|
|
46
|
-
twig: 'twig'
|
|
47
|
+
twig: 'twig',
|
|
48
|
+
sql: 'sql'
|
|
47
49
|
};
|
|
48
50
|
/* The highlighter id for the code of a file we only know by its path, such as a place another file refers to. */
|
|
49
51
|
export function shikiLanguageOfPath(path) {
|
package/dist/locales/en.json
CHANGED
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
"line": "Line {{line}}",
|
|
10
10
|
"none": "No information for this position"
|
|
11
11
|
},
|
|
12
|
+
"inlay": {
|
|
13
|
+
"failed": "The hint's action failed: {{message}}"
|
|
14
|
+
},
|
|
12
15
|
"edit": {
|
|
13
16
|
"staged_one": "{{count}} file changed and not saved yet",
|
|
14
17
|
"staged_other": "{{count}} files changed and not saved yet",
|
|
@@ -30,6 +33,10 @@
|
|
|
30
33
|
"otherFiles_other": "and {{count}} other files",
|
|
31
34
|
"moves_one": "moves {{count}} file",
|
|
32
35
|
"moves_other": "moves {{count}} files",
|
|
36
|
+
"creates_one": "creates {{count}} file",
|
|
37
|
+
"creates_other": "creates {{count}} files",
|
|
38
|
+
"deletes_one": "deletes {{count}} file",
|
|
39
|
+
"deletes_other": "deletes {{count}} files",
|
|
33
40
|
"groups": {
|
|
34
41
|
"quickfix": "Quick fix",
|
|
35
42
|
"rewrite": "Rewrite",
|
|
@@ -56,7 +63,12 @@
|
|
|
56
63
|
"cancel": "Cancel",
|
|
57
64
|
"apply": "Rename",
|
|
58
65
|
"suggestions": "Suggested names",
|
|
59
|
-
"useSuggestion": "Use suggestion"
|
|
66
|
+
"useSuggestion": "Use suggestion",
|
|
67
|
+
"operations": {
|
|
68
|
+
"create": "Creates",
|
|
69
|
+
"rename": "Moves",
|
|
70
|
+
"delete": "Deletes"
|
|
71
|
+
}
|
|
60
72
|
},
|
|
61
73
|
"navigation": {
|
|
62
74
|
"unavailable": "No language server answers that here",
|
|
@@ -70,6 +82,9 @@
|
|
|
70
82
|
"symbol": "Matches"
|
|
71
83
|
}
|
|
72
84
|
},
|
|
85
|
+
"panel": {
|
|
86
|
+
"resize": "Resize panel"
|
|
87
|
+
},
|
|
73
88
|
"peek": {
|
|
74
89
|
"unavailable": "No language server finds references in this file",
|
|
75
90
|
"none": "No references found",
|
|
@@ -84,6 +99,33 @@
|
|
|
84
99
|
"noDefinition": "No definition found",
|
|
85
100
|
"unavailableDefinition": "No language server finds definitions in this file"
|
|
86
101
|
},
|
|
102
|
+
"callHierarchy": {
|
|
103
|
+
"unavailable": "No language server finds calls in this file",
|
|
104
|
+
"none": "No symbol with calls found here",
|
|
105
|
+
"failed": "Could not look up the calls: {{message}}",
|
|
106
|
+
"label": "Call hierarchy",
|
|
107
|
+
"direction": "Direction",
|
|
108
|
+
"incoming": "Callers",
|
|
109
|
+
"outgoing": "Callees",
|
|
110
|
+
"close": "Close",
|
|
111
|
+
"loading": "Loading…",
|
|
112
|
+
"noIncoming": "No callers",
|
|
113
|
+
"noOutgoing": "No callees",
|
|
114
|
+
"retry": "Could not load the calls: {{message}}. Press to try again",
|
|
115
|
+
"more_one": "Show {{formatted}} more call ({{hidden}} left)",
|
|
116
|
+
"more_other": "Show {{formatted}} more calls ({{hidden}} left)",
|
|
117
|
+
"sites_one": "{{formatted}} call",
|
|
118
|
+
"sites_other": "{{formatted}} calls",
|
|
119
|
+
"site": "Call {{current}} of {{total}}",
|
|
120
|
+
"previousSite": "Previous call",
|
|
121
|
+
"nextSite": "Next call",
|
|
122
|
+
"announce": {
|
|
123
|
+
"incoming_one": "{{count}} caller of {{name}}",
|
|
124
|
+
"incoming_other": "{{count}} callers of {{name}}",
|
|
125
|
+
"outgoing_one": "{{count}} callee of {{name}}",
|
|
126
|
+
"outgoing_other": "{{count}} callees of {{name}}"
|
|
127
|
+
}
|
|
128
|
+
},
|
|
87
129
|
"codeVision": {
|
|
88
130
|
"usagesNone": "No usages",
|
|
89
131
|
"usages_one": "{{formatted}} usage",
|
|
@@ -126,20 +168,65 @@
|
|
|
126
168
|
"copy": "Copy",
|
|
127
169
|
"paste": "Paste"
|
|
128
170
|
},
|
|
171
|
+
"snippet": {
|
|
172
|
+
"choices": "Snippet choice"
|
|
173
|
+
},
|
|
129
174
|
"completion": {
|
|
175
|
+
"tooLarge": "Completion is too large to insert",
|
|
130
176
|
"insert": "Insert",
|
|
131
177
|
"replace": "Replace",
|
|
132
178
|
"details": "Details",
|
|
133
|
-
"none": "No suggestions"
|
|
179
|
+
"none": "No suggestions",
|
|
180
|
+
"transformBusy": "Snippet transforms are busy; try again",
|
|
181
|
+
"transformFailed": "Snippet transform could not be applied"
|
|
134
182
|
},
|
|
135
183
|
"history": {
|
|
136
184
|
"recent": "Recent locations"
|
|
185
|
+
},
|
|
186
|
+
"announce": {
|
|
187
|
+
"problem": "{{severity}}: {{message}}",
|
|
188
|
+
"severity": {
|
|
189
|
+
"error": "Error",
|
|
190
|
+
"warning": "Warning",
|
|
191
|
+
"info": "Information",
|
|
192
|
+
"hint": "Hint"
|
|
193
|
+
},
|
|
194
|
+
"field": "Field {{current}} of {{total}}",
|
|
195
|
+
"fieldChoices_one": "Field {{current}} of {{total}}, {{count}} choice",
|
|
196
|
+
"fieldChoices_other": "Field {{current}} of {{total}}, {{count}} choices"
|
|
197
|
+
},
|
|
198
|
+
"fold": {
|
|
199
|
+
"fold": "Fold lines {{start}} to {{end}}",
|
|
200
|
+
"unfold": "Unfold lines {{start}} to {{end}}"
|
|
201
|
+
},
|
|
202
|
+
"docs": {
|
|
203
|
+
"label": "Rendered documentation",
|
|
204
|
+
"labelNamed": "Rendered documentation: {{- summary}}",
|
|
205
|
+
"render": "Show rendered documentation",
|
|
206
|
+
"showSource": "Show documentation source",
|
|
207
|
+
"tags": {
|
|
208
|
+
"deprecated": "Deprecated",
|
|
209
|
+
"template": "Type parameters",
|
|
210
|
+
"param": "Parameters",
|
|
211
|
+
"property": "Properties",
|
|
212
|
+
"type": "Type",
|
|
213
|
+
"var": "Variable",
|
|
214
|
+
"return": "Returns",
|
|
215
|
+
"yield": "Yields",
|
|
216
|
+
"throws": "Throws",
|
|
217
|
+
"see": "See also",
|
|
218
|
+
"example": "Example",
|
|
219
|
+
"since": "Since",
|
|
220
|
+
"version": "Version",
|
|
221
|
+
"author": "Author"
|
|
222
|
+
}
|
|
137
223
|
}
|
|
138
224
|
},
|
|
139
225
|
"commands": {
|
|
140
226
|
"goToDefinition": "Go to definition",
|
|
141
227
|
"goToTypeDefinition": "Go to type definition",
|
|
142
228
|
"goToImplementation": "Go to implementation",
|
|
229
|
+
"callHierarchy": "Call hierarchy",
|
|
143
230
|
"peekReferences": "Peek references",
|
|
144
231
|
"renameSymbol": "Rename symbol",
|
|
145
232
|
"codeActions": "Code actions",
|
|
@@ -148,6 +235,7 @@
|
|
|
148
235
|
"find": {
|
|
149
236
|
"label": "Find",
|
|
150
237
|
"count": "{{current}} / {{total}}",
|
|
238
|
+
"countLimited": "{{current}} / {{total}}+",
|
|
151
239
|
"none": "No results",
|
|
152
240
|
"invalid": "Invalid regular expression",
|
|
153
241
|
"caseSensitive": "Match case",
|
|
@@ -166,7 +254,33 @@
|
|
|
166
254
|
"allLabel": "Replace all",
|
|
167
255
|
"preserveCase": "Preserve case",
|
|
168
256
|
"toggle": "Show replace",
|
|
169
|
-
"hide": "Hide replace"
|
|
257
|
+
"hide": "Hide replace",
|
|
258
|
+
"failed": {
|
|
259
|
+
"limit": "Too much to replace at once ({{edits}} edits or {{characters}} output characters)",
|
|
260
|
+
"timeout": "Replacement timed out; the text was not changed",
|
|
261
|
+
"unavailable": "Replacement needs the search worker",
|
|
262
|
+
"error": "Could not replace; the text was not changed"
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
"countMore": "{{total}}+ matches",
|
|
266
|
+
"searching": "Searching…",
|
|
267
|
+
"large": "Large document: press Enter to search",
|
|
268
|
+
"replaced": "Replaced {{count}} matches",
|
|
269
|
+
"failed": {
|
|
270
|
+
"timeout": "Search timed out; press Enter to retry",
|
|
271
|
+
"unavailable": "Regex search is unavailable",
|
|
272
|
+
"invalid": "Invalid regular expression",
|
|
273
|
+
"error": "Could not finish the search"
|
|
170
274
|
}
|
|
275
|
+
},
|
|
276
|
+
"diff": {
|
|
277
|
+
"hidden": "Show {{count}} hidden lines",
|
|
278
|
+
"contextLimit": "Context limit reached",
|
|
279
|
+
"oldSide": "Before",
|
|
280
|
+
"newSide": "After",
|
|
281
|
+
"noNewline": "No final newline",
|
|
282
|
+
"added": "Added, new line {{number}}",
|
|
283
|
+
"removed": "Removed, old line {{number}}",
|
|
284
|
+
"unchanged": "Old line {{old}}, new line {{new}}"
|
|
171
285
|
}
|
|
172
286
|
}
|