@adecore/editor-react 0.0.1 → 0.17.0-beta.2
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/LICENSE +110 -0
- package/README.md +60 -0
- package/dist/AnchoredPopup.d.ts +12 -0
- package/dist/AnchoredPopup.js +27 -0
- package/dist/AttributionCard.d.ts +14 -0
- package/dist/AttributionCard.js +5 -0
- package/dist/BaselineStatus.d.ts +4 -0
- package/dist/BaselineStatus.js +13 -0
- package/dist/ChangeReview.d.ts +12 -0
- package/dist/ChangeReview.js +27 -0
- package/dist/CodeAuthorsCard.d.ts +6 -0
- package/dist/CodeAuthorsCard.js +22 -0
- package/dist/CompletionPopup.d.ts +8 -0
- package/dist/CompletionPopup.js +103 -0
- package/dist/EditorContextMenu.d.ts +8 -0
- package/dist/EditorContextMenu.js +23 -0
- package/dist/EditorView.d.ts +16 -0
- package/dist/EditorView.js +27 -0
- package/dist/FindReplace.d.ts +6 -0
- package/dist/FindReplace.js +44 -0
- package/dist/HoverCard.d.ts +11 -0
- package/dist/HoverCard.js +38 -0
- package/dist/HoverSections.d.ts +21 -0
- package/dist/HoverSections.js +82 -0
- package/dist/LanguagePopups.d.ts +6 -0
- package/dist/LanguagePopups.js +38 -0
- package/dist/Markdown.d.ts +3 -0
- package/dist/Markdown.js +7 -0
- package/dist/PathText.d.ts +4 -0
- package/dist/PathText.js +12 -0
- package/dist/PeekPanel.d.ts +6 -0
- package/dist/PeekPanel.js +111 -0
- package/dist/PickPopup.d.ts +8 -0
- package/dist/PickPopup.js +33 -0
- package/dist/RenameCard.d.ts +9 -0
- package/dist/RenameCard.js +108 -0
- package/dist/SignatureCard.d.ts +4 -0
- package/dist/SignatureCard.js +8 -0
- package/dist/SymbolPicker.d.ts +6 -0
- package/dist/SymbolPicker.js +108 -0
- package/dist/code-actions-model.d.ts +23 -0
- package/dist/code-actions-model.js +114 -0
- package/dist/code-actions.d.ts +30 -0
- package/dist/code-actions.js +326 -0
- package/dist/code-authors.d.ts +18 -0
- package/dist/code-authors.js +79 -0
- package/dist/code-vision-model.d.ts +21 -0
- package/dist/code-vision-model.js +183 -0
- package/dist/code-vision.d.ts +48 -0
- package/dist/code-vision.js +236 -0
- package/dist/completion-call.d.ts +24 -0
- package/dist/completion-call.js +110 -0
- package/dist/completion-model.d.ts +51 -0
- package/dist/completion-model.js +353 -0
- package/dist/completion.d.ts +43 -0
- package/dist/completion.js +463 -0
- package/dist/conflict-model.d.ts +15 -0
- package/dist/conflict-model.js +36 -0
- package/dist/context-menu.d.ts +10 -0
- package/dist/context-menu.js +55 -0
- package/dist/definition-link.d.ts +14 -0
- package/dist/definition-link.js +99 -0
- package/dist/diagnostics-model.d.ts +15 -0
- package/dist/diagnostics-model.js +83 -0
- package/dist/diagnostics.d.ts +23 -0
- package/dist/diagnostics.js +95 -0
- package/dist/editor-language.d.ts +65 -0
- package/dist/editor-language.js +170 -0
- package/dist/editor-react.css +43 -0
- package/dist/find-query.d.ts +26 -0
- package/dist/find-query.js +51 -0
- package/dist/folding.d.ts +16 -0
- package/dist/folding.js +95 -0
- package/dist/highlight-layers.d.ts +10 -0
- package/dist/highlight-layers.js +30 -0
- package/dist/highlights.d.ts +16 -0
- package/dist/highlights.js +102 -0
- package/dist/history.d.ts +8 -0
- package/dist/history.js +46 -0
- package/dist/holder.d.ts +5 -0
- package/dist/holder.js +19 -0
- package/dist/host-types.d.ts +45 -0
- package/dist/host-types.js +1 -0
- package/dist/hover-content.d.ts +45 -0
- package/dist/hover-content.js +198 -0
- package/dist/hover.d.ts +32 -0
- package/dist/hover.js +260 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.js +28 -0
- package/dist/inlay-hints.d.ts +13 -0
- package/dist/inlay-hints.js +71 -0
- package/dist/language-ids.d.ts +3 -0
- package/dist/language-ids.js +49 -0
- package/dist/line-edits.d.ts +8 -0
- package/dist/line-edits.js +39 -0
- package/dist/locales/en.json +171 -0
- package/dist/locales/nl.json +171 -0
- package/dist/models.d.ts +20 -0
- package/dist/models.js +20 -0
- package/dist/navigation-history.d.ts +16 -0
- package/dist/navigation-history.js +53 -0
- package/dist/navigation.d.ts +23 -0
- package/dist/navigation.js +155 -0
- package/dist/paths.d.ts +1 -0
- package/dist/paths.js +3 -0
- package/dist/peek-model.d.ts +28 -0
- package/dist/peek-model.js +104 -0
- package/dist/peek.d.ts +30 -0
- package/dist/peek.js +215 -0
- package/dist/pick.d.ts +34 -0
- package/dist/pick.js +166 -0
- package/dist/popup-placement.d.ts +23 -0
- package/dist/popup-placement.js +34 -0
- package/dist/popups.d.ts +133 -0
- package/dist/popups.js +14 -0
- package/dist/project-language.d.ts +36 -0
- package/dist/project-language.js +190 -0
- package/dist/project-problems.d.ts +24 -0
- package/dist/project-problems.js +63 -0
- package/dist/proposal-model.d.ts +36 -0
- package/dist/proposal-model.js +197 -0
- package/dist/recent-choices.d.ts +10 -0
- package/dist/recent-choices.js +39 -0
- package/dist/refresher.d.ts +13 -0
- package/dist/refresher.js +50 -0
- package/dist/relative-time.d.ts +1 -0
- package/dist/relative-time.js +9 -0
- package/dist/rename-model.d.ts +25 -0
- package/dist/rename-model.js +93 -0
- package/dist/rename.d.ts +21 -0
- package/dist/rename.js +257 -0
- package/dist/rendering-context.d.ts +6 -0
- package/dist/rendering-context.js +5 -0
- package/dist/rendering.d.ts +6 -0
- package/dist/rendering.js +5 -0
- package/dist/review-group.d.ts +16 -0
- package/dist/review-group.js +54 -0
- package/dist/row-host.d.ts +42 -0
- package/dist/row-host.js +120 -0
- package/dist/selection-ranges.d.ts +7 -0
- package/dist/selection-ranges.js +26 -0
- package/dist/semantic-model.d.ts +4 -0
- package/dist/semantic-model.js +76 -0
- package/dist/semantic-tokens.d.ts +6 -0
- package/dist/semantic-tokens.js +38 -0
- package/dist/shortcut-keys.d.ts +91 -0
- package/dist/shortcut-keys.js +12 -0
- package/dist/signature-model.d.ts +18 -0
- package/dist/signature-model.js +33 -0
- package/dist/signature.d.ts +22 -0
- package/dist/signature.js +150 -0
- package/dist/snippet-session.d.ts +17 -0
- package/dist/snippet-session.js +110 -0
- package/dist/snippet.d.ts +11 -0
- package/dist/snippet.js +134 -0
- package/dist/symbol-links.d.ts +6 -0
- package/dist/symbol-links.js +50 -0
- package/dist/symbol-picker-model.d.ts +44 -0
- package/dist/symbol-picker-model.js +146 -0
- package/dist/symbol-picker.d.ts +12 -0
- package/dist/symbol-picker.js +76 -0
- package/dist/symbols.d.ts +11 -0
- package/dist/symbols.js +95 -0
- package/dist/testing.d.ts +57 -0
- package/dist/testing.js +156 -0
- package/dist/timers.d.ts +14 -0
- package/dist/timers.js +34 -0
- package/dist/use-problem-counts.d.ts +3 -0
- package/dist/use-problem-counts.js +20 -0
- package/dist/view-state.d.ts +21 -0
- package/dist/view-state.js +63 -0
- package/dist/word-diff.d.ts +1 -0
- package/dist/word-diff.js +32 -0
- package/dist/workspace-edit.d.ts +21 -0
- package/dist/workspace-edit.js +147 -0
- package/package.json +74 -2
- package/src/AnchoredPopup.tsx +51 -0
- package/src/AttributionCard.tsx +36 -0
- package/src/BaselineStatus.tsx +20 -0
- package/src/ChangeReview.tsx +43 -0
- package/src/CodeAuthorsCard.tsx +81 -0
- package/src/CompletionPopup.tsx +196 -0
- package/src/EditorContextMenu.tsx +67 -0
- package/src/EditorView.tsx +47 -0
- package/src/FindReplace.tsx +89 -0
- package/src/HoverCard.tsx +121 -0
- package/src/HoverSections.tsx +146 -0
- package/src/LanguagePopups.tsx +65 -0
- package/src/Markdown.tsx +11 -0
- package/src/PathText.tsx +18 -0
- package/src/PeekPanel.tsx +222 -0
- package/src/PickPopup.tsx +102 -0
- package/src/RenameCard.tsx +219 -0
- package/src/SignatureCard.tsx +42 -0
- package/src/SymbolPicker.tsx +219 -0
- package/src/code-actions-model.ts +146 -0
- package/src/code-actions.ts +384 -0
- package/src/code-authors.ts +104 -0
- package/src/code-vision-model.ts +230 -0
- package/src/code-vision.ts +269 -0
- package/src/completion-call.ts +139 -0
- package/src/completion-model.ts +440 -0
- package/src/completion.ts +500 -0
- package/src/conflict-model.ts +56 -0
- package/src/context-menu.ts +63 -0
- package/src/definition-link.ts +106 -0
- package/src/diagnostics-model.ts +102 -0
- package/src/diagnostics.ts +117 -0
- package/src/editor-language.ts +189 -0
- package/src/editor-react.css +43 -0
- package/src/find-query.ts +74 -0
- package/src/folding.ts +110 -0
- package/src/highlight-layers.ts +37 -0
- package/src/highlights.ts +123 -0
- package/src/history.ts +56 -0
- package/src/holder.ts +19 -0
- package/src/host-types.ts +52 -0
- package/src/hover-content.ts +264 -0
- package/src/hover.ts +280 -0
- package/src/index.ts +63 -0
- package/src/inlay-hints.ts +85 -0
- package/src/language-ids.ts +54 -0
- package/src/line-edits.ts +48 -0
- package/src/locales/en.json +171 -0
- package/src/locales/nl.json +171 -0
- package/src/models.ts +157 -0
- package/src/navigation-history.ts +69 -0
- package/src/navigation.ts +168 -0
- package/src/paths.ts +3 -0
- package/src/peek-model.ts +147 -0
- package/src/peek.ts +247 -0
- package/src/pick.ts +197 -0
- package/src/popup-placement.ts +64 -0
- package/src/popups.ts +204 -0
- package/src/project-language.ts +223 -0
- package/src/project-problems.ts +87 -0
- package/src/proposal-model.ts +259 -0
- package/src/recent-choices.ts +48 -0
- package/src/refresher.ts +56 -0
- package/src/relative-time.ts +10 -0
- package/src/rename-model.ts +130 -0
- package/src/rename.ts +290 -0
- package/src/rendering-context.ts +12 -0
- package/src/rendering.tsx +6 -0
- package/src/review-group.ts +70 -0
- package/src/row-host.ts +158 -0
- package/src/selection-ranges.ts +32 -0
- package/src/semantic-model.ts +80 -0
- package/src/semantic-tokens.ts +46 -0
- package/src/shortcut-keys.ts +21 -0
- package/src/signature-model.ts +49 -0
- package/src/signature.ts +164 -0
- package/src/snippet-session.ts +122 -0
- package/src/snippet.ts +147 -0
- package/src/symbol-links.ts +72 -0
- package/src/symbol-picker-model.ts +198 -0
- package/src/symbol-picker.ts +85 -0
- package/src/symbols.ts +110 -0
- package/src/testing.ts +240 -0
- package/src/timers.ts +45 -0
- package/src/use-problem-counts.ts +25 -0
- package/src/view-state.ts +88 -0
- package/src/word-diff.ts +35 -0
- package/src/workspace-edit.ts +194 -0
package/dist/hover.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { EditorPosition, EditorRange } from '@adecore/editor';
|
|
2
|
+
import type { EditorLanguage } from './editor-language.ts';
|
|
3
|
+
import { type Timers } from './timers.ts';
|
|
4
|
+
export declare class HoverFeature {
|
|
5
|
+
private readonly language;
|
|
6
|
+
private readonly timers;
|
|
7
|
+
private showTimer;
|
|
8
|
+
private countTimer;
|
|
9
|
+
private readonly counts;
|
|
10
|
+
private hideTimer;
|
|
11
|
+
private inCard;
|
|
12
|
+
private request;
|
|
13
|
+
private lookup;
|
|
14
|
+
private pinned;
|
|
15
|
+
private held;
|
|
16
|
+
constructor(language: EditorLanguage, timers?: Timers);
|
|
17
|
+
holdCard(inside: boolean): void;
|
|
18
|
+
quickInfo(): void;
|
|
19
|
+
showProblemsAt(position: EditorPosition): boolean;
|
|
20
|
+
keep(): void;
|
|
21
|
+
showRange(range: EditorRange): void;
|
|
22
|
+
hide(): void;
|
|
23
|
+
private moved;
|
|
24
|
+
private scheduleHide;
|
|
25
|
+
private ask;
|
|
26
|
+
private show;
|
|
27
|
+
private present;
|
|
28
|
+
private scheduleCount;
|
|
29
|
+
private count;
|
|
30
|
+
private showCount;
|
|
31
|
+
wordStart(position: EditorPosition): number;
|
|
32
|
+
}
|
package/dist/hover.js
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { rangeHolds } from './diagnostics-model.js';
|
|
2
|
+
import { hoverTextOf, isEmptyHover, locationsOf } from './hover-content.js';
|
|
3
|
+
import { wordRangeAt } from './rename-model.js';
|
|
4
|
+
import { isShortcut } from './shortcut-keys.js';
|
|
5
|
+
import { realTimers } from './timers.js';
|
|
6
|
+
const SHOW_DELAY_MS = 300;
|
|
7
|
+
const HIDE_DELAY_MS = 250;
|
|
8
|
+
/* How long a card has to stay up before the references of its symbol are counted, which can be a request over the whole project. */
|
|
9
|
+
const COUNT_DELAY_MS = 500;
|
|
10
|
+
const TOAST_ID = 'language-hover';
|
|
11
|
+
/*
|
|
12
|
+
* The card under a character the pointer rests on: the problems there, and what the language servers
|
|
13
|
+
* say about the symbol. It opens after the pointer has stayed a moment, stays while the pointer is on
|
|
14
|
+
* the word or on the card itself, and goes when the text, the scroll or the focus moves.
|
|
15
|
+
*/
|
|
16
|
+
export class HoverFeature {
|
|
17
|
+
language;
|
|
18
|
+
timers;
|
|
19
|
+
showTimer;
|
|
20
|
+
countTimer;
|
|
21
|
+
/* The counts asked for since the text last changed, by the symbol's position, so moving back to a name does not ask again. */
|
|
22
|
+
counts = new Map();
|
|
23
|
+
hideTimer;
|
|
24
|
+
inCard = false;
|
|
25
|
+
/* Counts every request, so an answer that arrives after the pointer moved on is dropped. */
|
|
26
|
+
request = 0;
|
|
27
|
+
lookup = null;
|
|
28
|
+
/* A card the keyboard opened: scrolling does not take it away, since it was never under a pointer. Moving the caret does. */
|
|
29
|
+
pinned = false;
|
|
30
|
+
/* The card is being read: an explanation runs in it or is shown, so the pointer going elsewhere neither closes nor replaces it. */
|
|
31
|
+
held = false;
|
|
32
|
+
constructor(language, timers = realTimers) {
|
|
33
|
+
this.language = language;
|
|
34
|
+
this.timers = timers;
|
|
35
|
+
const { editor } = language;
|
|
36
|
+
const offs = [
|
|
37
|
+
editor.onHover((hover) => this.moved(hover)),
|
|
38
|
+
editor.onTextChange(() => {
|
|
39
|
+
this.counts.clear();
|
|
40
|
+
this.hide();
|
|
41
|
+
}),
|
|
42
|
+
editor.onClick(() => {
|
|
43
|
+
if (this.held) {
|
|
44
|
+
this.hide();
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
}),
|
|
48
|
+
editor.onViewChange(() => {
|
|
49
|
+
if (!this.pinned) {
|
|
50
|
+
this.hide();
|
|
51
|
+
}
|
|
52
|
+
}),
|
|
53
|
+
editor.onCaret(() => {
|
|
54
|
+
if (this.pinned) {
|
|
55
|
+
this.hide();
|
|
56
|
+
}
|
|
57
|
+
}),
|
|
58
|
+
editor.onKeyDown((event) => {
|
|
59
|
+
if (isShortcut(this.language.shortcuts.quickInfo, event)) {
|
|
60
|
+
this.quickInfo();
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
if (event.key === 'Escape' && this.language.popups.getState().hover !== null) {
|
|
64
|
+
this.hide();
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
})
|
|
69
|
+
];
|
|
70
|
+
language.onDispose(() => {
|
|
71
|
+
for (const off of offs) {
|
|
72
|
+
off();
|
|
73
|
+
}
|
|
74
|
+
this.hide();
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
/* The pointer entered or left the card, which is where a link in it is clicked. */
|
|
78
|
+
holdCard(inside) {
|
|
79
|
+
this.inCard = inside;
|
|
80
|
+
if (inside) {
|
|
81
|
+
this.timers.clear(this.hideTimer);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
this.scheduleHide();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/* The card for the caret, as a pointer resting there would open it. */
|
|
88
|
+
quickInfo() {
|
|
89
|
+
const request = ++this.request;
|
|
90
|
+
this.timers.clear(this.showTimer);
|
|
91
|
+
void this.show(this.language.editor.getCaret(), request, true);
|
|
92
|
+
}
|
|
93
|
+
/* The problems at a position as a card, at once; what the servers say about the symbol is left for the pointer and Quick Info. */
|
|
94
|
+
showProblemsAt(position) {
|
|
95
|
+
const problems = this.language.diagnostics.at(position);
|
|
96
|
+
if (problems.length === 0) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
this.hide();
|
|
100
|
+
this.pinned = true;
|
|
101
|
+
this.present(position, problems, null, problems[0].diagnostic.range);
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
/* Keeps the card up until Escape, a press in the text, an edit or a move of the caret. */
|
|
105
|
+
keep() {
|
|
106
|
+
this.pinned = true;
|
|
107
|
+
this.held = true;
|
|
108
|
+
this.timers.clear(this.hideTimer);
|
|
109
|
+
}
|
|
110
|
+
/* A card of its own for a range, such as the explanation of a selection, which stays like the card Quick Info opens. */
|
|
111
|
+
showRange(range) {
|
|
112
|
+
this.hide();
|
|
113
|
+
this.keep();
|
|
114
|
+
this.present(range.start, [], null, range);
|
|
115
|
+
}
|
|
116
|
+
hide() {
|
|
117
|
+
this.pinned = false;
|
|
118
|
+
this.held = false;
|
|
119
|
+
this.request++;
|
|
120
|
+
this.lookup?.abort();
|
|
121
|
+
this.timers.clear(this.showTimer);
|
|
122
|
+
this.timers.clear(this.hideTimer);
|
|
123
|
+
this.timers.clear(this.countTimer);
|
|
124
|
+
this.inCard = false;
|
|
125
|
+
if (this.language.popups.getState().hover !== null) {
|
|
126
|
+
this.language.popups.setState({ hover: null });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
moved(hover) {
|
|
130
|
+
if (this.held) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
this.timers.clear(this.showTimer);
|
|
134
|
+
if (hover === null) {
|
|
135
|
+
this.request++;
|
|
136
|
+
this.scheduleHide();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const shown = this.language.popups.getState().hover;
|
|
140
|
+
if (shown !== null && rangeHolds(shown.subject, hover.position)) {
|
|
141
|
+
this.timers.clear(this.hideTimer);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
this.scheduleHide();
|
|
145
|
+
const request = ++this.request;
|
|
146
|
+
this.showTimer = this.timers.set(() => void this.show(hover.position, request, false), SHOW_DELAY_MS);
|
|
147
|
+
}
|
|
148
|
+
scheduleHide() {
|
|
149
|
+
this.timers.clear(this.hideTimer);
|
|
150
|
+
if (this.language.popups.getState().hover === null || this.inCard || this.held) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
this.hideTimer = this.timers.set(() => {
|
|
154
|
+
if (!this.inCard) {
|
|
155
|
+
this.hide();
|
|
156
|
+
}
|
|
157
|
+
}, HIDE_DELAY_MS);
|
|
158
|
+
}
|
|
159
|
+
/* What the servers say at a position: the hover text and the definition, each of which may be missing without the other. */
|
|
160
|
+
async ask(position, signal) {
|
|
161
|
+
const { service } = this.language.project;
|
|
162
|
+
const { uri } = this.language;
|
|
163
|
+
if (!service.supports('textDocument/hover', uri)) {
|
|
164
|
+
return { info: null, range: null };
|
|
165
|
+
}
|
|
166
|
+
const [hover, definition] = await Promise.all([
|
|
167
|
+
service.hover(uri, position, { signal }).catch(() => null),
|
|
168
|
+
service.supports('textDocument/definition', uri) ? service.definition(uri, position, { signal }).catch(() => null) : null
|
|
169
|
+
]);
|
|
170
|
+
const text = hover === null ? null : hoverTextOf(hover);
|
|
171
|
+
if (hover === null || text === null || isEmptyHover(text)) {
|
|
172
|
+
return { info: null, range: null };
|
|
173
|
+
}
|
|
174
|
+
const { editor } = this.language;
|
|
175
|
+
const line = editor.textInRange({ start: { line: position.line, character: 0 }, end: { line: position.line, character: Number.MAX_SAFE_INTEGER } });
|
|
176
|
+
const word = wordRangeAt(line, position);
|
|
177
|
+
return {
|
|
178
|
+
info: {
|
|
179
|
+
text,
|
|
180
|
+
definition: locationsOf(definition)[0] ?? null,
|
|
181
|
+
word: word === null ? '' : line.slice(word.start.character, word.end.character),
|
|
182
|
+
references: null
|
|
183
|
+
},
|
|
184
|
+
range: hover.range ?? null
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
async show(position, request, pinned) {
|
|
188
|
+
const problems = this.language.diagnostics.at(position);
|
|
189
|
+
this.lookup?.abort();
|
|
190
|
+
const controller = new AbortController();
|
|
191
|
+
this.lookup = controller;
|
|
192
|
+
const { info, range } = await this.ask(position, controller.signal);
|
|
193
|
+
if (request !== this.request) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (problems.length === 0 && info === null) {
|
|
197
|
+
this.hide();
|
|
198
|
+
if (pinned) {
|
|
199
|
+
this.language.project.host.notify?.({ id: TOAST_ID, kind: 'error', title: this.language.project.i18n.t('editor:language.hover.none') });
|
|
200
|
+
}
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
this.pinned = pinned;
|
|
204
|
+
this.present(position, problems, info, problems[0]?.diagnostic.range ?? range);
|
|
205
|
+
if (info !== null) {
|
|
206
|
+
this.scheduleCount(position, request, controller.signal);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
present(position, problems, info, range) {
|
|
210
|
+
const subject = range ?? { start: position, end: position };
|
|
211
|
+
this.timers.clear(this.hideTimer);
|
|
212
|
+
this.language.popups.setState({
|
|
213
|
+
hover: {
|
|
214
|
+
anchor: subject.start.line === position.line ? subject.start : position,
|
|
215
|
+
position,
|
|
216
|
+
subject,
|
|
217
|
+
problems,
|
|
218
|
+
info
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
/* The references of the symbol are counted once the card has stayed up a while, or at once when the text has not changed since they were. */
|
|
223
|
+
scheduleCount(position, request, signal) {
|
|
224
|
+
const key = `${position.line}:${this.wordStart(position)}`;
|
|
225
|
+
const known = this.counts.get(key);
|
|
226
|
+
if (known !== undefined) {
|
|
227
|
+
this.showCount(known, request);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
this.timers.clear(this.countTimer);
|
|
231
|
+
this.countTimer = this.timers.set(() => void this.count(position, key, request, signal), COUNT_DELAY_MS);
|
|
232
|
+
}
|
|
233
|
+
/* How many places use the symbol, which can take a while in a large project and so arrives after the card does. */
|
|
234
|
+
async count(position, key, request, signal) {
|
|
235
|
+
const { service } = this.language.project;
|
|
236
|
+
if (request !== this.request || !service.supports('textDocument/references', this.language.uri)) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const found = await service.references(this.language.uri, position, false, { signal }).catch(() => null);
|
|
240
|
+
if (found === null) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
this.counts.set(key, found.length);
|
|
244
|
+
this.showCount(found.length, request);
|
|
245
|
+
}
|
|
246
|
+
showCount(count, request) {
|
|
247
|
+
const shown = this.language.popups.getState().hover;
|
|
248
|
+
if (request === this.request && shown?.info != null) {
|
|
249
|
+
this.language.popups.setState({ hover: { ...shown, info: { ...shown.info, references: count } } });
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/* Where the word at a position starts, which names the symbol for the cache. */
|
|
253
|
+
wordStart(position) {
|
|
254
|
+
const line = this.language.editor.textInRange({
|
|
255
|
+
start: { line: position.line, character: 0 },
|
|
256
|
+
end: { line: position.line, character: Number.MAX_SAFE_INTEGER }
|
|
257
|
+
});
|
|
258
|
+
return wordRangeAt(line, position)?.start.character ?? position.character;
|
|
259
|
+
}
|
|
260
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export { EditorLanguage } from './editor-language.ts';
|
|
2
|
+
export { ProjectLanguage } from './project-language.ts';
|
|
3
|
+
export type { LanguageDocumentHandle } from './project-language.ts';
|
|
4
|
+
export type { LanguageHost, DiskText, GitBlameCommit, GitBlameResult, EditorNotification, RenameSuggestionsRequest } from './host-types.ts';
|
|
5
|
+
export { EditorView } from './EditorView.tsx';
|
|
6
|
+
export { createHolder } from './holder.ts';
|
|
7
|
+
export type { EditorViewProps } from './EditorView.tsx';
|
|
8
|
+
export { LanguagePopups } from './LanguagePopups.tsx';
|
|
9
|
+
export { AnchoredPopup } from './AnchoredPopup.tsx';
|
|
10
|
+
export { HoverCard } from './HoverCard.tsx';
|
|
11
|
+
export { CompletionPopup } from './CompletionPopup.tsx';
|
|
12
|
+
export { SignatureCard } from './SignatureCard.tsx';
|
|
13
|
+
export { RenameCard } from './RenameCard.tsx';
|
|
14
|
+
export { PickPopup } from './PickPopup.tsx';
|
|
15
|
+
export { PeekPanel } from './PeekPanel.tsx';
|
|
16
|
+
export { SymbolPicker } from './SymbolPicker.tsx';
|
|
17
|
+
export { CodeAuthorsCard } from './CodeAuthorsCard.tsx';
|
|
18
|
+
export { EditorContextMenu } from './EditorContextMenu.tsx';
|
|
19
|
+
export { EditorRenderingProvider } from './rendering.tsx';
|
|
20
|
+
export type { EditorRendering } from './rendering-context.ts';
|
|
21
|
+
export { FindReplace } from './FindReplace.tsx';
|
|
22
|
+
export { ChangeReview } from './ChangeReview.tsx';
|
|
23
|
+
export type { ChangeReviewProps } from './ChangeReview.tsx';
|
|
24
|
+
export { ProjectProblems, countsOf, compareRows } from './project-problems.ts';
|
|
25
|
+
export type { ProblemFile, ProblemRow } from './project-problems.ts';
|
|
26
|
+
export { applyWorkspaceEdit } from './workspace-edit.ts';
|
|
27
|
+
export type { ProjectFiles, StagedFile, WorkspaceEditHost } from './workspace-edit.ts';
|
|
28
|
+
export { useProblemCounts } from './use-problem-counts.ts';
|
|
29
|
+
export type { ProblemCounts } from './diagnostics.ts';
|
|
30
|
+
export type { BlameSource, CodeVisionSettings } from './code-vision.ts';
|
|
31
|
+
export type { Problem } from './diagnostics-model.ts';
|
|
32
|
+
export type { PopupState, PopupStore, HoverInfo, HoverView, CompletionView, CompletionDocs, CompletionRow, SignatureView, RenameView, RenameFileView, PeekView, PickView, PickGroup, PickRow, PickPreview, SymbolsView, MenuView, AuthorsView } from './popups.ts';
|
|
33
|
+
export { RowHost, LineActionHost } from './row-host.ts';
|
|
34
|
+
export type { HostedRow, HostedAction } from './row-host.ts';
|
|
35
|
+
export { HighlightLayers, highlightLayers } from './highlight-layers.ts';
|
|
36
|
+
export { NavigationHistory } from './navigation-history.ts';
|
|
37
|
+
export type { Place } from './navigation-history.ts';
|
|
38
|
+
export { openingPlace, viewStates, followViewStates, viewStateKey, forgetMovesFrom } from './view-state.ts';
|
|
39
|
+
export type { ViewState, RevealLineRequest } from './view-state.ts';
|
|
40
|
+
export { lspLanguageIdOf, shikiLanguageOf, shikiLanguageOfPath } from './language-ids.ts';
|
|
41
|
+
export { AttributionCard } from './AttributionCard.tsx';
|
|
42
|
+
export type { AttributionCardProps } from './AttributionCard.tsx';
|
|
43
|
+
export { ReviewGroup, joinReviewGroup } from './review-group.ts';
|
|
44
|
+
export type { ReviewMember } from './review-group.ts';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export { EditorLanguage } from './editor-language.js';
|
|
2
|
+
export { ProjectLanguage } from './project-language.js';
|
|
3
|
+
export { EditorView } from './EditorView.js';
|
|
4
|
+
export { createHolder } from './holder.js';
|
|
5
|
+
export { LanguagePopups } from './LanguagePopups.js';
|
|
6
|
+
export { AnchoredPopup } from './AnchoredPopup.js';
|
|
7
|
+
export { HoverCard } from './HoverCard.js';
|
|
8
|
+
export { CompletionPopup } from './CompletionPopup.js';
|
|
9
|
+
export { SignatureCard } from './SignatureCard.js';
|
|
10
|
+
export { RenameCard } from './RenameCard.js';
|
|
11
|
+
export { PickPopup } from './PickPopup.js';
|
|
12
|
+
export { PeekPanel } from './PeekPanel.js';
|
|
13
|
+
export { SymbolPicker } from './SymbolPicker.js';
|
|
14
|
+
export { CodeAuthorsCard } from './CodeAuthorsCard.js';
|
|
15
|
+
export { EditorContextMenu } from './EditorContextMenu.js';
|
|
16
|
+
export { EditorRenderingProvider } from './rendering.js';
|
|
17
|
+
export { FindReplace } from './FindReplace.js';
|
|
18
|
+
export { ChangeReview } from './ChangeReview.js';
|
|
19
|
+
export { ProjectProblems, countsOf, compareRows } from './project-problems.js';
|
|
20
|
+
export { applyWorkspaceEdit } from './workspace-edit.js';
|
|
21
|
+
export { useProblemCounts } from './use-problem-counts.js';
|
|
22
|
+
export { RowHost, LineActionHost } from './row-host.js';
|
|
23
|
+
export { HighlightLayers, highlightLayers } from './highlight-layers.js';
|
|
24
|
+
export { NavigationHistory } from './navigation-history.js';
|
|
25
|
+
export { openingPlace, viewStates, followViewStates, viewStateKey, forgetMovesFrom } from './view-state.js';
|
|
26
|
+
export { lspLanguageIdOf, shikiLanguageOf, shikiLanguageOfPath } from './language-ids.js';
|
|
27
|
+
export { AttributionCard } from './AttributionCard.js';
|
|
28
|
+
export { ReviewGroup, joinReviewGroup } from './review-group.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { EditorInlayHint, EditorPosition, EditorRange } from '@adecore/editor';
|
|
2
|
+
import type { InlayHint } from '@adecore/lsp';
|
|
3
|
+
import type { EditorLanguage } from './editor-language.ts';
|
|
4
|
+
import { type Timers } from './timers.ts';
|
|
5
|
+
export declare function hintLabelOf(hint: InlayHint): string;
|
|
6
|
+
export declare function editorHintsOf(hints: readonly InlayHint[]): EditorInlayHint[];
|
|
7
|
+
export declare function withMargin(visible: EditorRange, endOfDocument: EditorPosition, endOfLine: (line: number) => EditorPosition): EditorRange;
|
|
8
|
+
export declare function covers(asked: EditorRange, visible: EditorRange): boolean;
|
|
9
|
+
export declare class InlayHintsFeature {
|
|
10
|
+
private readonly refresher;
|
|
11
|
+
private asked;
|
|
12
|
+
constructor(language: EditorLanguage, timers?: Timers);
|
|
13
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Refresher } from './refresher.js';
|
|
2
|
+
import { realTimers } from './timers.js';
|
|
3
|
+
const METHOD = 'textDocument/inlayHint';
|
|
4
|
+
const PAUSE_MS = 300;
|
|
5
|
+
/* Lines asked for above and below what is in view, so a little scrolling stays inside what is already drawn. */
|
|
6
|
+
const MARGIN_LINES = 60;
|
|
7
|
+
/* A hint's label is a string or a list of parts, which are read as one text. */
|
|
8
|
+
export function hintLabelOf(hint) {
|
|
9
|
+
return typeof hint.label === 'string' ? hint.label : hint.label.map((part) => part.value).join('');
|
|
10
|
+
}
|
|
11
|
+
export function editorHintsOf(hints) {
|
|
12
|
+
return hints
|
|
13
|
+
.map((hint) => ({ position: hint.position, label: hintLabelOf(hint) }))
|
|
14
|
+
.filter((hint) => hint.label !== '')
|
|
15
|
+
.sort((left, right) => left.position.line - right.position.line || left.position.character - right.position.character);
|
|
16
|
+
}
|
|
17
|
+
/* The lines in view with a margin of lines around them, from the start of the first to the end of the last. */
|
|
18
|
+
export function withMargin(visible, endOfDocument, endOfLine) {
|
|
19
|
+
const first = Math.max(0, visible.start.line - MARGIN_LINES);
|
|
20
|
+
const last = Math.min(endOfDocument.line, visible.end.line + MARGIN_LINES);
|
|
21
|
+
return { start: { line: first, character: 0 }, end: last === endOfDocument.line ? endOfDocument : endOfLine(last) };
|
|
22
|
+
}
|
|
23
|
+
/* Whether what was asked for already holds every line in view. */
|
|
24
|
+
export function covers(asked, visible) {
|
|
25
|
+
return asked.start.line <= visible.start.line && asked.end.line >= visible.end.line;
|
|
26
|
+
}
|
|
27
|
+
/*
|
|
28
|
+
* Types and parameter names the servers infer, drawn in the text as soft pills. They are asked for the
|
|
29
|
+
* lines in view and a margin around them after a pause in typing or scrolling; until the answer comes the
|
|
30
|
+
* last ones stay where their text went, so nothing blinks while typing. Scrolling asks again only once the
|
|
31
|
+
* view leaves the lines that were asked for.
|
|
32
|
+
*/
|
|
33
|
+
export class InlayHintsFeature {
|
|
34
|
+
refresher;
|
|
35
|
+
/* The lines the hints on screen were asked for. */
|
|
36
|
+
asked = null;
|
|
37
|
+
constructor(language, timers = realTimers) {
|
|
38
|
+
const { editor, project, uri } = language;
|
|
39
|
+
this.refresher = new Refresher(async (signal) => {
|
|
40
|
+
const { service } = project;
|
|
41
|
+
if (!service.supports(METHOD, uri)) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const range = withMargin(editor.getVisibleRange(), editor.positionAt(Number.MAX_SAFE_INTEGER), (line) => ({
|
|
45
|
+
line,
|
|
46
|
+
character: editor.textInRange({ start: { line, character: 0 }, end: { line, character: Number.MAX_SAFE_INTEGER } }).length
|
|
47
|
+
}));
|
|
48
|
+
this.asked = range;
|
|
49
|
+
const hints = await service.inlayHints(uri, range, { signal });
|
|
50
|
+
if (!signal.aborted) {
|
|
51
|
+
editor.setInlayHints(hints === null ? [] : editorHintsOf(hints));
|
|
52
|
+
}
|
|
53
|
+
}, PAUSE_MS, timers);
|
|
54
|
+
const edits = editor.onTextChange(() => this.refresher.later());
|
|
55
|
+
const scrolls = editor.onViewChange(() => {
|
|
56
|
+
if (this.asked !== null && !covers(this.asked, editor.getVisibleRange())) {
|
|
57
|
+
this.refresher.later();
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
const providers = language.onProvidersChanged(() => {
|
|
61
|
+
this.refresher.now();
|
|
62
|
+
});
|
|
63
|
+
language.onDispose(() => {
|
|
64
|
+
edits();
|
|
65
|
+
scrolls();
|
|
66
|
+
providers();
|
|
67
|
+
this.refresher.dispose();
|
|
68
|
+
editor.setInlayHints([]);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/* The highlighter ids `fs.read` answers with, by the id LSP names the same language. */
|
|
2
|
+
const LSP_IDS = {
|
|
3
|
+
typescript: 'typescript',
|
|
4
|
+
tsx: 'typescriptreact',
|
|
5
|
+
javascript: 'javascript',
|
|
6
|
+
jsx: 'javascriptreact',
|
|
7
|
+
vue: 'vue',
|
|
8
|
+
php: 'php',
|
|
9
|
+
css: 'css',
|
|
10
|
+
scss: 'scss',
|
|
11
|
+
less: 'less',
|
|
12
|
+
html: 'html',
|
|
13
|
+
json: 'json',
|
|
14
|
+
jsonc: 'jsonc',
|
|
15
|
+
yaml: 'yaml',
|
|
16
|
+
python: 'python',
|
|
17
|
+
shellscript: 'shellscript',
|
|
18
|
+
docker: 'dockerfile'
|
|
19
|
+
};
|
|
20
|
+
/* The LSP language id of a file, or null when no language server here knows its language. */
|
|
21
|
+
export function lspLanguageIdOf(language) {
|
|
22
|
+
return language === undefined ? null : (LSP_IDS[language] ?? null);
|
|
23
|
+
}
|
|
24
|
+
const SHIKI_IDS = { typescriptreact: 'tsx', javascriptreact: 'jsx' };
|
|
25
|
+
/* The highlighter id of an LSP language id, for code a server sends back to be drawn. */
|
|
26
|
+
export function shikiLanguageOf(lspLanguageId) {
|
|
27
|
+
return SHIKI_IDS[lspLanguageId] ?? lspLanguageId;
|
|
28
|
+
}
|
|
29
|
+
const PATH_IDS = {
|
|
30
|
+
ts: 'typescript',
|
|
31
|
+
mts: 'typescript',
|
|
32
|
+
cts: 'typescript',
|
|
33
|
+
tsx: 'tsx',
|
|
34
|
+
js: 'javascript',
|
|
35
|
+
mjs: 'javascript',
|
|
36
|
+
cjs: 'javascript',
|
|
37
|
+
jsx: 'jsx',
|
|
38
|
+
vue: 'vue',
|
|
39
|
+
php: 'php',
|
|
40
|
+
json: 'json',
|
|
41
|
+
css: 'css',
|
|
42
|
+
scss: 'scss',
|
|
43
|
+
html: 'html',
|
|
44
|
+
md: 'markdown'
|
|
45
|
+
};
|
|
46
|
+
/* The highlighter id for the code of a file we only know by its path, such as a place another file refers to. */
|
|
47
|
+
export function shikiLanguageOfPath(path) {
|
|
48
|
+
return PATH_IDS[path.slice(path.lastIndexOf('.') + 1).toLowerCase()] ?? 'text';
|
|
49
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { EditorContentChange } from '@adecore/editor';
|
|
2
|
+
export interface LineReplacement {
|
|
3
|
+
readonly from: number;
|
|
4
|
+
readonly to: number;
|
|
5
|
+
readonly lines: readonly string[];
|
|
6
|
+
}
|
|
7
|
+
export declare function replaceLines(text: string, replacement: LineReplacement): EditorContentChange;
|
|
8
|
+
export declare function replaceAllLines(text: string, replacements: readonly LineReplacement[]): EditorContentChange[];
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { shapeOf, splitLines } from '@adecore/merge';
|
|
2
|
+
/*
|
|
3
|
+
* The edit that makes the lines `from` up to `to` of `text` into `lines`. A file that does not end on a
|
|
4
|
+
* newline has no line break after its last line, so the break before it goes along when the last lines
|
|
5
|
+
* leave and comes with the new ones when they are put at the end.
|
|
6
|
+
*/
|
|
7
|
+
export function replaceLines(text, replacement) {
|
|
8
|
+
const { from, to, lines } = replacement;
|
|
9
|
+
const eol = shapeOf(text).eol;
|
|
10
|
+
const raw = text.split('\n');
|
|
11
|
+
const count = splitLines(text).length;
|
|
12
|
+
const body = lines.join(eol);
|
|
13
|
+
if (to < count || text === '' || text.endsWith('\n')) {
|
|
14
|
+
return { range: { start: { line: from, character: 0 }, end: { line: to, character: 0 } }, text: lines.length === 0 ? '' : `${body}${eol}` };
|
|
15
|
+
}
|
|
16
|
+
const end = { line: count - 1, character: raw[count - 1].length };
|
|
17
|
+
if (from === count) {
|
|
18
|
+
return { range: { start: end, end }, text: lines.length === 0 ? '' : `${eol}${body}` };
|
|
19
|
+
}
|
|
20
|
+
if (lines.length === 0 && from > 0) {
|
|
21
|
+
return { range: { start: { line: from - 1, character: raw[from - 1].length }, end }, text: '' };
|
|
22
|
+
}
|
|
23
|
+
return { range: { start: { line: from, character: 0 }, end }, text: body };
|
|
24
|
+
}
|
|
25
|
+
/* Several replacements of one text as the edits an editor applies at once; replacements that touch are one edit. */
|
|
26
|
+
export function replaceAllLines(text, replacements) {
|
|
27
|
+
const ordered = [...replacements].sort((left, right) => left.from - right.from);
|
|
28
|
+
const merged = [];
|
|
29
|
+
for (const next of ordered) {
|
|
30
|
+
const last = merged.at(-1);
|
|
31
|
+
if (last !== undefined && last.to >= next.from) {
|
|
32
|
+
merged[merged.length - 1] = { from: last.from, to: Math.max(last.to, next.to), lines: [...last.lines, ...next.lines] };
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
merged.push(next);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return merged.map((replacement) => replaceLines(text, replacement));
|
|
39
|
+
}
|