@adecore/editor-react 0.39.0 → 0.40.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CallHierarchyPanel.js +30 -55
- package/dist/CodeAuthorsCard.js +0 -1
- package/dist/CompletionPopup.js +150 -181
- package/dist/DiffView.d.ts +1 -1
- package/dist/DiffView.js +2 -29
- package/dist/PickPopup.js +54 -80
- package/dist/SplitDiffView.js +27 -57
- package/dist/SymbolPicker.js +2 -8
- package/dist/code-actions-model.js +2 -9
- package/dist/code-actions.js +10 -11
- package/dist/code-vision.js +2 -1
- package/dist/completion-model.js +2 -2
- package/dist/completion.js +13 -34
- package/dist/definition-link.d.ts +1 -0
- package/dist/definition-link.js +22 -18
- package/dist/diagnostics-model.d.ts +1 -0
- package/dist/diagnostics-model.js +3 -1
- package/dist/diagnostics.js +1 -2
- package/dist/diff-syntax.d.ts +1 -0
- package/dist/diff-syntax.js +31 -0
- package/dist/doc-comment-model.d.ts +1 -0
- package/dist/doc-comment-model.js +1 -1
- package/dist/folding.js +2 -3
- package/dist/format-on-type.js +6 -10
- package/dist/hover-content.d.ts +0 -2
- package/dist/hover-content.js +1 -9
- package/dist/inlay-hints.d.ts +0 -1
- package/dist/inlay-hints.js +11 -41
- package/dist/navigation.js +4 -14
- package/dist/peek.js +2 -12
- package/dist/popup-focus.d.ts +3 -1
- package/dist/popup-focus.js +33 -0
- package/dist/recent-choices.d.ts +0 -1
- package/dist/recent-choices.js +0 -4
- package/dist/rendered-docs.d.ts +1 -0
- package/dist/rendered-docs.js +15 -28
- package/dist/snippet-field-transforms.d.ts +5 -0
- package/dist/snippet-field-transforms.js +27 -16
- package/dist/snippet-session.js +8 -20
- package/dist/symbol-links.js +3 -10
- package/dist/symbol-picker-model.d.ts +1 -0
- package/dist/symbol-picker-model.js +7 -0
- package/dist/unique.d.ts +10 -0
- package/dist/unique.js +15 -0
- package/package.json +9 -9
- package/src/AnchoredPopup.tsx +2 -0
- package/src/BaselineStatus.tsx +1 -0
- package/src/CallHierarchyPanel.tsx +16 -6
- package/src/ChangeReview.tsx +7 -0
- package/src/CodeAuthorsCard.tsx +2 -5
- package/src/CompletionPopup.tsx +21 -15
- package/src/DiffView.tsx +53 -30
- package/src/EditorContextMenu.tsx +7 -0
- package/src/EditorView.tsx +28 -0
- package/src/FindReplace.tsx +8 -0
- package/src/HoverCard.tsx +8 -0
- package/src/HoverSections.tsx +11 -0
- package/src/LanguagePopups.tsx +1 -0
- package/src/PanelResizeHandle.tsx +8 -0
- package/src/PathText.tsx +1 -0
- package/src/PeekPanel.tsx +13 -0
- package/src/PickPopup.tsx +7 -6
- package/src/RenameCard.tsx +13 -0
- package/src/RenderedDocs.tsx +11 -0
- package/src/SplitDiffView.tsx +91 -56
- package/src/SymbolPicker.tsx +10 -18
- package/src/call-hierarchy-model.ts +10 -0
- package/src/call-hierarchy.ts +82 -0
- package/src/code-actions-model.ts +21 -9
- package/src/code-actions.ts +83 -7
- package/src/code-authors.ts +20 -0
- package/src/code-vision-model.ts +37 -0
- package/src/code-vision.ts +47 -3
- package/src/completion-call.ts +19 -0
- package/src/completion-model.ts +58 -2
- package/src/completion.ts +156 -30
- package/src/conflict-model.ts +4 -0
- package/src/context-menu-rows.ts +1 -0
- package/src/context-menu.ts +7 -0
- package/src/definition-link.ts +42 -18
- package/src/diagnostics-model.ts +19 -1
- package/src/diagnostics.ts +31 -2
- package/src/diff-expansion.ts +9 -0
- package/src/diff-overlays.tsx +8 -0
- package/src/diff-syntax.ts +54 -0
- package/src/diff-wordmarks.ts +8 -0
- package/src/doc-comment-model.ts +51 -1
- package/src/editor-language.ts +11 -0
- package/src/find-query.ts +7 -0
- package/src/folding.ts +10 -3
- package/src/format-on-type.ts +23 -10
- package/src/highlight-layers.ts +3 -0
- package/src/highlights.ts +18 -0
- package/src/history.ts +11 -0
- package/src/holder.ts +3 -0
- package/src/hover-content.ts +34 -17
- package/src/hover.ts +57 -0
- package/src/index.ts +4 -0
- package/src/inlay-hints.ts +86 -47
- package/src/key-priority.ts +2 -0
- package/src/line-edits.ts +8 -0
- package/src/models.ts +6 -0
- package/src/navigation-history.ts +5 -0
- package/src/navigation.ts +28 -13
- package/src/panel-size.ts +7 -0
- package/src/patch-decorations.ts +39 -0
- package/src/patch-layout.ts +47 -0
- package/src/peek-model.ts +21 -0
- package/src/peek.ts +49 -13
- package/src/pick.ts +35 -0
- package/src/popup-focus.ts +11 -1
- package/src/popup-placement.ts +5 -0
- package/src/popups.ts +2 -6
- package/src/project-language.ts +33 -0
- package/src/project-problems.ts +11 -0
- package/src/proposal-model.ts +40 -0
- package/src/recent-choices.ts +3 -5
- package/src/refresher.ts +3 -0
- package/src/rename-model.ts +28 -0
- package/src/rename.ts +56 -0
- package/src/rendered-docs.ts +89 -19
- package/src/review-group.ts +4 -0
- package/src/row-host.ts +12 -0
- package/src/selection-ranges.ts +6 -0
- package/src/semantic-delta.ts +7 -0
- package/src/semantic-model.ts +11 -0
- package/src/semantic-tokens.ts +37 -0
- package/src/shortcut-keys.ts +3 -0
- package/src/signature-model.ts +7 -0
- package/src/signature.ts +27 -0
- package/src/snippet-field-transforms.ts +48 -13
- package/src/snippet-indent.ts +10 -0
- package/src/snippet-range-order.ts +5 -0
- package/src/snippet-session.ts +173 -31
- package/src/snippet-transform-client.ts +14 -0
- package/src/snippet-transform-format.ts +98 -0
- package/src/snippet-transform-protocol.ts +5 -0
- package/src/snippet-transform-queue.ts +53 -0
- package/src/snippet-transform-worker.ts +14 -0
- package/src/snippet-variables.ts +9 -0
- package/src/snippet.ts +107 -0
- package/src/split-alignment.ts +10 -0
- package/src/split-scroll.ts +8 -0
- package/src/symbol-links.ts +9 -12
- package/src/symbol-picker-model.ts +25 -0
- package/src/symbol-picker.ts +9 -0
- package/src/symbols.ts +21 -0
- package/src/testing.ts +7 -0
- package/src/timers.ts +5 -0
- package/src/unique.ts +23 -0
- package/src/use-problem-counts.ts +4 -0
- package/src/view-state.ts +9 -0
- package/src/word-diff.ts +5 -0
- package/src/workspace-edit.ts +84 -0
package/src/rendered-docs.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { findDocComments, type DocComment, type EditorPosition, type EditorRange, type EditorReplacement, type EditorTrackedRange } from '@adecore/editor';
|
|
2
|
+
import { comparePositions } from './diagnostics-model.ts';
|
|
2
3
|
import { KEY_PRIORITY } from './key-priority.ts';
|
|
3
4
|
import type { EditorLanguage } from './editor-language.ts';
|
|
4
5
|
import { parseDocComment, type ParsedDocComment } from './doc-comment-model.ts';
|
|
6
|
+
import { lineTextOf } from './line-text.ts';
|
|
5
7
|
import { isShortcut } from './shortcut-keys.ts';
|
|
6
8
|
import { realTimers, type Timers } from './timers.ts';
|
|
7
9
|
|
|
@@ -9,8 +11,10 @@ const OWNER = 'rendered-docs';
|
|
|
9
11
|
const DOCS_PREFIX = 'docs-';
|
|
10
12
|
const SUMMARY_WORDS = 6;
|
|
11
13
|
const SETTLE_MS = 100;
|
|
14
|
+
|
|
12
15
|
/* What a row is taken to be tall until its content is in and measured. */
|
|
13
16
|
const ESTIMATED_LINE_HEIGHT = 20;
|
|
17
|
+
|
|
14
18
|
const MAX_ESTIMATED_LINES = 8;
|
|
15
19
|
const MAX_PARSED = 200;
|
|
16
20
|
|
|
@@ -43,23 +47,31 @@ function touches(range: EditorRange, comment: DocComment): boolean {
|
|
|
43
47
|
if (range.end.line < comment.startLine || range.start.line > comment.endLine) {
|
|
44
48
|
return false;
|
|
45
49
|
}
|
|
46
|
-
|
|
50
|
+
|
|
51
|
+
const empty = comparePositions(range.start, range.end) === 0;
|
|
52
|
+
|
|
47
53
|
if (!empty && range.end.line === comment.startLine && range.end.character === 0) {
|
|
48
54
|
return false;
|
|
49
55
|
}
|
|
56
|
+
|
|
50
57
|
return empty || range.start.line >= comment.startLine || range.end.line <= comment.endLine;
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
/* The first words of what a comment says, to tell its row from the others for a person who hears it. */
|
|
54
61
|
function summaryOf(body: string): string {
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
|
|
62
|
+
const lines = body.split('\n');
|
|
63
|
+
const lead = lines.findIndex((line) => /^\s*@/.test(line));
|
|
64
|
+
const text = (lead === -1 ? body : lines.slice(0, lead).join('\n')).replace(/[`*_#>[\]()]/g, ' ');
|
|
65
|
+
|
|
66
|
+
return text
|
|
67
|
+
.split(/\s+/)
|
|
68
|
+
.filter((word) => word !== '')
|
|
69
|
+
.slice(0, SUMMARY_WORDS)
|
|
70
|
+
.join(' ');
|
|
59
71
|
}
|
|
60
72
|
|
|
61
73
|
function samePlace(range: EditorRange | null, position: EditorPosition): boolean {
|
|
62
|
-
return range !== null && range.start
|
|
74
|
+
return range !== null && comparePositions(range.start, position) === 0;
|
|
63
75
|
}
|
|
64
76
|
|
|
65
77
|
/*
|
|
@@ -79,8 +91,10 @@ export class RenderedDocsFeature {
|
|
|
79
91
|
private readonly markerLines = new Map<string, number>();
|
|
80
92
|
private readonly parsed = new Map<string, ParsedDocComment>();
|
|
81
93
|
private indexes = new Map<DocComment, number>();
|
|
94
|
+
|
|
82
95
|
/* Comments a reveal showed as source until the caret has settled where it was going, or the comment has scrolled out of view if the caret never moves. */
|
|
83
96
|
private readonly revealed = new Set<DocComment>();
|
|
97
|
+
|
|
84
98
|
private readonly listeners = new Set<() => void>();
|
|
85
99
|
private version = 0;
|
|
86
100
|
private shown: { version: number; entries: readonly RenderedDocEntry[] } | null = null;
|
|
@@ -88,14 +102,17 @@ export class RenderedDocsFeature {
|
|
|
88
102
|
private caretTimer: unknown;
|
|
89
103
|
private drawn = '';
|
|
90
104
|
private marked = '';
|
|
105
|
+
|
|
91
106
|
/* The comments are from before the last edit, so nothing is decided from them until they are read again. */
|
|
92
107
|
private stale = false;
|
|
108
|
+
|
|
93
109
|
private disposed = false;
|
|
94
110
|
|
|
95
111
|
constructor(language: EditorLanguage, timers: Timers = realTimers) {
|
|
96
112
|
this.language = language;
|
|
97
113
|
this.timers = timers;
|
|
98
114
|
const { editor } = language;
|
|
115
|
+
|
|
99
116
|
const offs = [
|
|
100
117
|
editor.onTextChange(() => {
|
|
101
118
|
this.stale = true;
|
|
@@ -105,6 +122,7 @@ export class RenderedDocsFeature {
|
|
|
105
122
|
// The caret is told before the text change that moved it, so what the lines are is decided a moment later.
|
|
106
123
|
editor.onCaret(() => {
|
|
107
124
|
this.timers.clear(this.caretTimer);
|
|
125
|
+
|
|
108
126
|
this.caretTimer = this.timers.set(() => {
|
|
109
127
|
this.revealed.clear();
|
|
110
128
|
this.publish();
|
|
@@ -112,6 +130,7 @@ export class RenderedDocsFeature {
|
|
|
112
130
|
}),
|
|
113
131
|
editor.onGutterMarker((id) => {
|
|
114
132
|
const line = this.markerLines.get(id);
|
|
133
|
+
|
|
115
134
|
if (line !== undefined) {
|
|
116
135
|
this.toggleAt(line);
|
|
117
136
|
}
|
|
@@ -124,37 +143,42 @@ export class RenderedDocsFeature {
|
|
|
124
143
|
this.releaseRevealed();
|
|
125
144
|
})
|
|
126
145
|
];
|
|
146
|
+
|
|
127
147
|
language.onDispose(() => {
|
|
128
148
|
this.disposed = true;
|
|
149
|
+
|
|
129
150
|
for (const off of offs) {
|
|
130
151
|
off();
|
|
131
152
|
}
|
|
153
|
+
|
|
132
154
|
this.timers.clear(this.settleTimer);
|
|
133
155
|
this.timers.clear(this.caretTimer);
|
|
134
|
-
|
|
135
|
-
override.tracked.dispose();
|
|
136
|
-
}
|
|
137
|
-
this.overrides = [];
|
|
156
|
+
this.clearOverrides();
|
|
138
157
|
editor.setReplacements?.([], OWNER);
|
|
139
158
|
editor.setGutterMarkers([], OWNER);
|
|
140
159
|
this.containers.clear();
|
|
141
160
|
this.bump();
|
|
142
161
|
});
|
|
162
|
+
|
|
143
163
|
this.read();
|
|
144
164
|
}
|
|
145
165
|
|
|
146
166
|
/* Whether every documentation comment is drawn as text until a person switches it. Changing it puts every comment back under it. */
|
|
147
167
|
configure(settings: RenderedDocsSettings): void {
|
|
148
168
|
const enabled = settings.enabled !== false;
|
|
169
|
+
|
|
149
170
|
if (settings.renderDocComments === this.settings.renderDocComments && enabled === this.enabled) {
|
|
150
171
|
return;
|
|
151
172
|
}
|
|
173
|
+
|
|
152
174
|
const sameRendering = settings.renderDocComments === this.settings.renderDocComments;
|
|
153
175
|
this.settings = settings;
|
|
154
176
|
this.enabled = enabled;
|
|
177
|
+
|
|
155
178
|
if (!sameRendering) {
|
|
156
179
|
this.clearOverrides();
|
|
157
180
|
}
|
|
181
|
+
|
|
158
182
|
this.publish();
|
|
159
183
|
}
|
|
160
184
|
|
|
@@ -171,34 +195,39 @@ export class RenderedDocsFeature {
|
|
|
171
195
|
/* Switches the comment the caret is in between formatted and source. False when the caret is in none. */
|
|
172
196
|
toggle(): boolean {
|
|
173
197
|
this.settle();
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
return false;
|
|
177
|
-
}
|
|
178
|
-
this.setRendered(comment, !this.wantsRendered(comment));
|
|
179
|
-
return true;
|
|
198
|
+
|
|
199
|
+
return this.switch(this.at(this.language.editor.getCaret().line));
|
|
180
200
|
}
|
|
181
201
|
|
|
182
202
|
/* Switches the comment that starts on a line, which is what its gutter button does. */
|
|
183
203
|
toggleAt(startLine: number): boolean {
|
|
184
204
|
this.settle();
|
|
185
|
-
|
|
205
|
+
|
|
206
|
+
return this.switch(this.comments.find((candidate) => candidate.startLine === startLine));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
private switch(comment: DocComment | undefined): boolean {
|
|
186
210
|
if (!this.enabled || comment === undefined || !this.canRender(comment)) {
|
|
187
211
|
return false;
|
|
188
212
|
}
|
|
213
|
+
|
|
189
214
|
this.setRendered(comment, !this.wantsRendered(comment));
|
|
215
|
+
|
|
190
216
|
return true;
|
|
191
217
|
}
|
|
192
218
|
|
|
193
219
|
/* The elements of the rows now in view, for a host that draws them itself; `RenderedDocs` does. */
|
|
194
220
|
entries(): readonly RenderedDocEntry[] {
|
|
195
221
|
const result: RenderedDocEntry[] = [];
|
|
222
|
+
|
|
196
223
|
for (const [id, container] of this.containers) {
|
|
197
224
|
const comment = this.comments[Number(id.slice(DOCS_PREFIX.length))];
|
|
225
|
+
|
|
198
226
|
if (comment !== undefined) {
|
|
199
227
|
result.push({ id, comment, parsed: this.parse(comment), container });
|
|
200
228
|
}
|
|
201
229
|
}
|
|
230
|
+
|
|
202
231
|
return result;
|
|
203
232
|
}
|
|
204
233
|
|
|
@@ -211,11 +240,13 @@ export class RenderedDocsFeature {
|
|
|
211
240
|
if (this.shown?.version !== this.version) {
|
|
212
241
|
this.shown = { version: this.version, entries: this.entries() };
|
|
213
242
|
}
|
|
243
|
+
|
|
214
244
|
return this.shown.entries;
|
|
215
245
|
}
|
|
216
246
|
|
|
217
247
|
subscribe(listener: () => void): () => void {
|
|
218
248
|
this.listeners.add(listener);
|
|
249
|
+
|
|
219
250
|
return () => {
|
|
220
251
|
this.listeners.delete(listener);
|
|
221
252
|
};
|
|
@@ -224,6 +255,7 @@ export class RenderedDocsFeature {
|
|
|
224
255
|
/* Whether names in `{@link}` and `@see` can be followed, which takes a server that finds symbols by name. */
|
|
225
256
|
get canFollow(): boolean {
|
|
226
257
|
const { project, uri } = this.language;
|
|
258
|
+
|
|
227
259
|
return project.service.supports('workspace/symbol', uri);
|
|
228
260
|
}
|
|
229
261
|
|
|
@@ -248,8 +280,10 @@ export class RenderedDocsFeature {
|
|
|
248
280
|
if (!this.enabled || !this.canRender(comment)) {
|
|
249
281
|
return false;
|
|
250
282
|
}
|
|
283
|
+
|
|
251
284
|
const start = startOf(comment);
|
|
252
285
|
const override = this.overrides.find((candidate) => samePlace(candidate.tracked.get(), start));
|
|
286
|
+
|
|
253
287
|
return override?.rendered ?? this.settings.renderDocComments;
|
|
254
288
|
}
|
|
255
289
|
|
|
@@ -264,17 +298,21 @@ export class RenderedDocsFeature {
|
|
|
264
298
|
private setRendered(comment: DocComment, rendered: boolean): void {
|
|
265
299
|
const start = startOf(comment);
|
|
266
300
|
const existing = this.overrides.find((candidate) => samePlace(candidate.tracked.get(), start));
|
|
301
|
+
|
|
267
302
|
if (existing !== undefined) {
|
|
268
303
|
existing.tracked.dispose();
|
|
269
304
|
this.overrides = this.overrides.filter((candidate) => candidate !== existing);
|
|
270
305
|
}
|
|
306
|
+
|
|
271
307
|
if (rendered !== this.settings.renderDocComments) {
|
|
272
308
|
const { editor } = this.language;
|
|
273
309
|
this.overrides.push({ tracked: editor.trackRange({ start, end: editor.positionAt(comment.to) }), rendered });
|
|
274
310
|
}
|
|
311
|
+
|
|
275
312
|
if (rendered) {
|
|
276
313
|
this.leave(comment);
|
|
277
314
|
}
|
|
315
|
+
|
|
278
316
|
this.publish();
|
|
279
317
|
}
|
|
280
318
|
|
|
@@ -282,15 +320,19 @@ export class RenderedDocsFeature {
|
|
|
282
320
|
private leave(comment: DocComment): void {
|
|
283
321
|
const { editor } = this.language;
|
|
284
322
|
const selections = editor.getSelections();
|
|
323
|
+
|
|
285
324
|
if (!selections.some((range) => touches(range, comment))) {
|
|
286
325
|
return;
|
|
287
326
|
}
|
|
327
|
+
|
|
288
328
|
const count = editor.positionAt(Number.MAX_SAFE_INTEGER).line + 1;
|
|
289
329
|
const line = comment.endLine + 1 < count ? comment.endLine + 1 : comment.startLine - 1;
|
|
330
|
+
|
|
290
331
|
if (line < 0) {
|
|
291
332
|
return;
|
|
292
333
|
}
|
|
293
|
-
|
|
334
|
+
|
|
335
|
+
const text = lineTextOf(editor, line);
|
|
294
336
|
const character = line > comment.endLine ? (/^[ \t]*/.exec(text)?.[0].length ?? 0) : text.length;
|
|
295
337
|
const place = { line, character };
|
|
296
338
|
editor.setSelections(selections.map((range) => (touches(range, comment) ? { start: place, end: place } : range)));
|
|
@@ -306,8 +348,10 @@ export class RenderedDocsFeature {
|
|
|
306
348
|
if (this.revealed.size === 0) {
|
|
307
349
|
return;
|
|
308
350
|
}
|
|
351
|
+
|
|
309
352
|
const { start, end } = this.language.editor.getVisibleRange();
|
|
310
353
|
const away = [...this.revealed].filter((comment) => comment.endLine < start.line || comment.startLine > end.line);
|
|
354
|
+
|
|
311
355
|
if (away.length > 0) {
|
|
312
356
|
away.forEach((comment) => this.revealed.delete(comment));
|
|
313
357
|
this.publish();
|
|
@@ -318,6 +362,7 @@ export class RenderedDocsFeature {
|
|
|
318
362
|
for (const override of this.overrides) {
|
|
319
363
|
override.tracked.dispose();
|
|
320
364
|
}
|
|
365
|
+
|
|
321
366
|
this.overrides = [];
|
|
322
367
|
}
|
|
323
368
|
|
|
@@ -333,34 +378,42 @@ export class RenderedDocsFeature {
|
|
|
333
378
|
if (this.disposed) {
|
|
334
379
|
return;
|
|
335
380
|
}
|
|
381
|
+
|
|
336
382
|
this.stale = false;
|
|
337
383
|
const { editor, languageId } = this.language;
|
|
338
384
|
this.comments = findDocComments(editor.getText(), languageId);
|
|
339
385
|
this.indexes = new Map(this.comments.map((comment, index) => [comment, index]));
|
|
340
386
|
this.revealed.clear();
|
|
341
387
|
const starts = this.comments.map(startOf);
|
|
388
|
+
|
|
342
389
|
this.overrides = this.overrides.filter((override) => {
|
|
343
390
|
const kept = starts.some((start) => samePlace(override.tracked.get(), start));
|
|
391
|
+
|
|
344
392
|
if (!kept) {
|
|
345
393
|
override.tracked.dispose();
|
|
346
394
|
}
|
|
395
|
+
|
|
347
396
|
return kept;
|
|
348
397
|
});
|
|
398
|
+
|
|
349
399
|
// An edit that replaced everything leaves the rows on offsets at the end, which only a new set of rows repairs.
|
|
350
400
|
this.publish(true);
|
|
351
401
|
this.bump();
|
|
352
402
|
}
|
|
353
403
|
|
|
354
404
|
private parse(comment: DocComment): ParsedDocComment {
|
|
355
|
-
const key =
|
|
405
|
+
const key = comment.body;
|
|
356
406
|
let result = this.parsed.get(key);
|
|
407
|
+
|
|
357
408
|
if (result === undefined) {
|
|
358
409
|
if (this.parsed.size >= MAX_PARSED) {
|
|
359
410
|
this.parsed.delete(this.parsed.keys().next().value!);
|
|
360
411
|
}
|
|
412
|
+
|
|
361
413
|
result = parseDocComment(comment.body, { php: /^php$/i.test(this.language.languageId) });
|
|
362
414
|
this.parsed.set(key, result);
|
|
363
415
|
}
|
|
416
|
+
|
|
364
417
|
return result;
|
|
365
418
|
}
|
|
366
419
|
|
|
@@ -369,13 +422,18 @@ export class RenderedDocsFeature {
|
|
|
369
422
|
if (this.disposed || this.stale) {
|
|
370
423
|
return;
|
|
371
424
|
}
|
|
425
|
+
|
|
372
426
|
const { editor } = this.language;
|
|
427
|
+
|
|
373
428
|
const t = (key: string, options?: Record<string, string>): string => this.language.project.i18n.t(`editor:language.docs.${key}`, options);
|
|
429
|
+
|
|
374
430
|
const rendered = this.comments.filter((comment) => this.isRendered(comment));
|
|
375
431
|
const signature = rendered.map((comment) => `${this.idOf(comment)}@${comment.startLine}-${comment.endLine}`).join('|');
|
|
432
|
+
|
|
376
433
|
if (force || signature !== this.drawn) {
|
|
377
434
|
this.drawn = signature;
|
|
378
435
|
const lineHeight = Math.max(editor.getBaseLineHeight?.() ?? 0, ESTIMATED_LINE_HEIGHT);
|
|
436
|
+
|
|
379
437
|
const rows = rendered.map((comment): EditorReplacement => ({
|
|
380
438
|
id: this.idOf(comment),
|
|
381
439
|
startLine: comment.startLine,
|
|
@@ -385,23 +443,31 @@ export class RenderedDocsFeature {
|
|
|
385
443
|
onReveal: () => this.reveal(comment),
|
|
386
444
|
render: (container) => this.mounted(this.idOf(comment), container)
|
|
387
445
|
}));
|
|
446
|
+
|
|
388
447
|
const kept = new Set(rows.map((row) => row.id));
|
|
448
|
+
|
|
389
449
|
for (const id of [...this.containers.keys()]) {
|
|
390
450
|
if (!kept.has(id)) {
|
|
391
451
|
this.containers.delete(id);
|
|
392
452
|
}
|
|
393
453
|
}
|
|
454
|
+
|
|
394
455
|
editor.setReplacements?.(rows, OWNER);
|
|
395
456
|
this.bump();
|
|
396
457
|
}
|
|
458
|
+
|
|
397
459
|
this.markerLines.clear();
|
|
460
|
+
|
|
398
461
|
const markers = (this.enabled ? this.comments : []).map((comment) => {
|
|
399
462
|
const id = `docs:${comment.startLine}`;
|
|
400
463
|
this.markerLines.set(id, comment.startLine);
|
|
401
464
|
const on = this.wantsRendered(comment);
|
|
465
|
+
|
|
402
466
|
return { id, line: comment.startLine, label: t(on ? 'showSource' : 'render'), icon: 'book-open' as const, pressed: on };
|
|
403
467
|
});
|
|
468
|
+
|
|
404
469
|
const marking = markers.map((marker) => `${marker.line}${marker.pressed}`).join('|');
|
|
470
|
+
|
|
405
471
|
if (marking !== this.marked) {
|
|
406
472
|
this.marked = marking;
|
|
407
473
|
editor.setGutterMarkers(markers, OWNER);
|
|
@@ -410,6 +476,7 @@ export class RenderedDocsFeature {
|
|
|
410
476
|
|
|
411
477
|
private labelOf(comment: DocComment, t: (key: string, options?: Record<string, string>) => string): string {
|
|
412
478
|
const summary = summaryOf(comment.body);
|
|
479
|
+
|
|
413
480
|
return summary === '' ? t('label') : t('labelNamed', { summary });
|
|
414
481
|
}
|
|
415
482
|
|
|
@@ -422,12 +489,14 @@ export class RenderedDocsFeature {
|
|
|
422
489
|
/* The editor drops the element of a row that scrolled out of view, and what React drew into it goes with it. The element just made is not in the page yet. */
|
|
423
490
|
private sweep(fresh?: HTMLElement): void {
|
|
424
491
|
let removed = false;
|
|
492
|
+
|
|
425
493
|
for (const [id, container] of this.containers) {
|
|
426
494
|
if (container !== fresh && !container.isConnected) {
|
|
427
495
|
this.containers.delete(id);
|
|
428
496
|
removed = true;
|
|
429
497
|
}
|
|
430
498
|
}
|
|
499
|
+
|
|
431
500
|
if (removed && fresh === undefined) {
|
|
432
501
|
this.bump();
|
|
433
502
|
}
|
|
@@ -435,6 +504,7 @@ export class RenderedDocsFeature {
|
|
|
435
504
|
|
|
436
505
|
private bump(): void {
|
|
437
506
|
this.version++;
|
|
507
|
+
|
|
438
508
|
for (const listener of [...this.listeners]) {
|
|
439
509
|
listener();
|
|
440
510
|
}
|
package/src/review-group.ts
CHANGED
|
@@ -17,6 +17,7 @@ export class ReviewGroup {
|
|
|
17
17
|
|
|
18
18
|
join(member: ReviewMember): () => void {
|
|
19
19
|
this.members.add(member);
|
|
20
|
+
|
|
20
21
|
return () => {
|
|
21
22
|
this.members.delete(member);
|
|
22
23
|
};
|
|
@@ -50,14 +51,17 @@ export function joinReviewGroup(key: string): { group: ReviewGroup; leave(): voi
|
|
|
50
51
|
groups.set(key, held);
|
|
51
52
|
held.holders++;
|
|
52
53
|
let left = false;
|
|
54
|
+
|
|
53
55
|
return {
|
|
54
56
|
group: held.group,
|
|
55
57
|
leave: () => {
|
|
56
58
|
if (left) {
|
|
57
59
|
return;
|
|
58
60
|
}
|
|
61
|
+
|
|
59
62
|
left = true;
|
|
60
63
|
held.holders--;
|
|
64
|
+
|
|
61
65
|
if (held.holders === 0 && groups.get(key) === held) {
|
|
62
66
|
groups.delete(key);
|
|
63
67
|
}
|
package/src/row-host.ts
CHANGED
|
@@ -40,11 +40,13 @@ abstract class ContainerHost {
|
|
|
40
40
|
if (this.shown?.version !== this.version) {
|
|
41
41
|
this.shown = { version: this.version, containers: new Map(this.containers) };
|
|
42
42
|
}
|
|
43
|
+
|
|
43
44
|
return this.shown.containers;
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
subscribe(listener: () => void): () => void {
|
|
47
48
|
this.listeners.add(listener);
|
|
49
|
+
|
|
48
50
|
return () => {
|
|
49
51
|
this.listeners.delete(listener);
|
|
50
52
|
};
|
|
@@ -59,13 +61,16 @@ abstract class ContainerHost {
|
|
|
59
61
|
if (signature === this.signature) {
|
|
60
62
|
return false;
|
|
61
63
|
}
|
|
64
|
+
|
|
62
65
|
this.signature = signature;
|
|
63
66
|
const kept = new Set(ids);
|
|
67
|
+
|
|
64
68
|
for (const id of [...this.containers.keys()]) {
|
|
65
69
|
if (!kept.has(id)) {
|
|
66
70
|
this.containers.delete(id);
|
|
67
71
|
}
|
|
68
72
|
}
|
|
73
|
+
|
|
69
74
|
return true;
|
|
70
75
|
}
|
|
71
76
|
|
|
@@ -76,6 +81,7 @@ abstract class ContainerHost {
|
|
|
76
81
|
|
|
77
82
|
protected bump(): void {
|
|
78
83
|
this.version++;
|
|
84
|
+
|
|
79
85
|
for (const listener of [...this.listeners]) {
|
|
80
86
|
listener();
|
|
81
87
|
}
|
|
@@ -90,6 +96,7 @@ abstract class ContainerHost {
|
|
|
90
96
|
export class RowHost extends ContainerHost {
|
|
91
97
|
set(rows: readonly HostedRow[]): void {
|
|
92
98
|
const signature = rows.map((row) => `${row.id}@${row.line}${row.placement}:${row.height}:${row.fixed}:${row.filler}`).join('|');
|
|
99
|
+
|
|
93
100
|
if (
|
|
94
101
|
!this.replace(
|
|
95
102
|
signature,
|
|
@@ -98,6 +105,7 @@ export class RowHost extends ContainerHost {
|
|
|
98
105
|
) {
|
|
99
106
|
return;
|
|
100
107
|
}
|
|
108
|
+
|
|
101
109
|
this.editor.setWidgets(
|
|
102
110
|
rows.map((row) => ({
|
|
103
111
|
id: row.filler ? `${row.id}-filler` : row.id,
|
|
@@ -115,6 +123,7 @@ export class RowHost extends ContainerHost {
|
|
|
115
123
|
})),
|
|
116
124
|
this.owner
|
|
117
125
|
);
|
|
126
|
+
|
|
118
127
|
this.bump();
|
|
119
128
|
}
|
|
120
129
|
|
|
@@ -131,6 +140,7 @@ export class RowHost extends ContainerHost {
|
|
|
131
140
|
export class LineActionHost extends ContainerHost {
|
|
132
141
|
set(actions: readonly HostedAction[]): void {
|
|
133
142
|
const signature = actions.map((action) => `${action.id}@${action.line}`).join('|');
|
|
143
|
+
|
|
134
144
|
if (
|
|
135
145
|
!this.replace(
|
|
136
146
|
signature,
|
|
@@ -139,6 +149,7 @@ export class LineActionHost extends ContainerHost {
|
|
|
139
149
|
) {
|
|
140
150
|
return;
|
|
141
151
|
}
|
|
152
|
+
|
|
142
153
|
this.editor.setLineActions(
|
|
143
154
|
actions.map((action) => ({
|
|
144
155
|
id: action.id,
|
|
@@ -147,6 +158,7 @@ export class LineActionHost extends ContainerHost {
|
|
|
147
158
|
})),
|
|
148
159
|
this.owner
|
|
149
160
|
);
|
|
161
|
+
|
|
150
162
|
this.bump();
|
|
151
163
|
}
|
|
152
164
|
|
package/src/selection-ranges.ts
CHANGED
|
@@ -7,9 +7,11 @@ const METHOD = 'textDocument/selectionRange';
|
|
|
7
7
|
/* The links of one answer, the smallest range first. */
|
|
8
8
|
export function selectionChainOf(selection: SelectionRange): EditorRange[] {
|
|
9
9
|
const chain: EditorRange[] = [];
|
|
10
|
+
|
|
10
11
|
for (let link: SelectionRange | undefined = selection; link; link = link.parent) {
|
|
11
12
|
chain.push(link.range);
|
|
12
13
|
}
|
|
14
|
+
|
|
13
15
|
return chain;
|
|
14
16
|
}
|
|
15
17
|
|
|
@@ -20,13 +22,17 @@ export function selectionChainOf(selection: SelectionRange): EditorRange[] {
|
|
|
20
22
|
export class SelectionRangesFeature {
|
|
21
23
|
constructor(language: EditorLanguage) {
|
|
22
24
|
const { editor, project, uri } = language;
|
|
25
|
+
|
|
23
26
|
editor.setSelectionRanges(async (positions) => {
|
|
24
27
|
if (!project.service.supports(METHOD, uri)) {
|
|
25
28
|
return null;
|
|
26
29
|
}
|
|
30
|
+
|
|
27
31
|
const chains = await project.service.selectionRanges(uri, positions);
|
|
32
|
+
|
|
28
33
|
return chains === null || chains.length !== positions.length ? null : chains.map(selectionChainOf);
|
|
29
34
|
});
|
|
35
|
+
|
|
30
36
|
language.onDispose(() => editor.setSelectionRanges(null));
|
|
31
37
|
}
|
|
32
38
|
}
|
package/src/semantic-delta.ts
CHANGED
|
@@ -8,22 +8,29 @@ import type { SemanticTokensDelta } from '@adecore/lsp';
|
|
|
8
8
|
export function applySemanticEdits(data: readonly number[], edits: SemanticTokensDelta['edits']): number[] | null {
|
|
9
9
|
const result: number[] = [];
|
|
10
10
|
let cursor = 0;
|
|
11
|
+
|
|
11
12
|
for (const edit of edits) {
|
|
12
13
|
const { start, deleteCount } = edit;
|
|
13
14
|
const inserted = edit.data ?? [];
|
|
15
|
+
|
|
14
16
|
if (!Number.isInteger(start) || !Number.isInteger(deleteCount) || start < cursor || deleteCount < 0 || start + deleteCount > data.length) {
|
|
15
17
|
return null;
|
|
16
18
|
}
|
|
19
|
+
|
|
17
20
|
for (let at = cursor; at < start; at++) {
|
|
18
21
|
result.push(data[at]!);
|
|
19
22
|
}
|
|
23
|
+
|
|
20
24
|
for (const value of inserted) {
|
|
21
25
|
result.push(value);
|
|
22
26
|
}
|
|
27
|
+
|
|
23
28
|
cursor = start + deleteCount;
|
|
24
29
|
}
|
|
30
|
+
|
|
25
31
|
for (let at = cursor; at < data.length; at++) {
|
|
26
32
|
result.push(data[at]!);
|
|
27
33
|
}
|
|
34
|
+
|
|
28
35
|
return result.length % 5 === 0 ? result : null;
|
|
29
36
|
}
|
package/src/semantic-model.ts
CHANGED
|
@@ -34,9 +34,11 @@ export function scopesOf(type: string, modifiers: ReadonlySet<string>, scoped =
|
|
|
34
34
|
if (modifiers.has('static')) {
|
|
35
35
|
return ['entity.name.function.static'];
|
|
36
36
|
}
|
|
37
|
+
|
|
37
38
|
if (modifiers.has('declaration')) {
|
|
38
39
|
return ['entity.name.function'];
|
|
39
40
|
}
|
|
41
|
+
|
|
40
42
|
return [type === 'method' ? 'entity.name.function.method' : 'entity.name.function.call'];
|
|
41
43
|
case 'decorator':
|
|
42
44
|
return ['meta.decorator'];
|
|
@@ -46,6 +48,7 @@ export function scopesOf(type: string, modifiers: ReadonlySet<string>, scoped =
|
|
|
46
48
|
if (scoped) {
|
|
47
49
|
return modifiers.has('local') ? null : ['variable.other.constant'];
|
|
48
50
|
}
|
|
51
|
+
|
|
49
52
|
return modifiers.has('readonly') ? ['variable.other.constant'] : null;
|
|
50
53
|
default:
|
|
51
54
|
return null;
|
|
@@ -71,25 +74,33 @@ export function decodeSemanticTokens(tokens: SemanticTokens, legend: SemanticTok
|
|
|
71
74
|
const scoped = legend.tokenModifiers.includes('local');
|
|
72
75
|
let line = 0;
|
|
73
76
|
let character = 0;
|
|
77
|
+
|
|
74
78
|
for (let at = 0; at + 4 < data.length; at += 5) {
|
|
75
79
|
const deltaLine = data[at]!;
|
|
76
80
|
line += deltaLine;
|
|
77
81
|
character = deltaLine === 0 ? character + data[at + 1]! : data[at + 1]!;
|
|
78
82
|
const type = legend.tokenTypes[data[at + 3]!];
|
|
83
|
+
|
|
79
84
|
if (type === undefined) {
|
|
80
85
|
continue;
|
|
81
86
|
}
|
|
87
|
+
|
|
82
88
|
const bits = data[at + 4]!;
|
|
83
89
|
const modifiers = new Set(legend.tokenModifiers.filter((_, index) => (bits & (1 << index)) !== 0));
|
|
84
90
|
const scopes = scopesOf(type, modifiers, scoped);
|
|
91
|
+
|
|
85
92
|
if (scopes !== null) {
|
|
86
93
|
result.push({ line, character, length: data[at + 2]!, scopes });
|
|
94
|
+
|
|
87
95
|
continue;
|
|
88
96
|
}
|
|
97
|
+
|
|
89
98
|
const embedded = EMBEDDED_SCOPES[type];
|
|
99
|
+
|
|
90
100
|
if (embedded !== undefined) {
|
|
91
101
|
result.push({ line, character, length: data[at + 2]!, scopes: embedded, within: 'string' });
|
|
92
102
|
}
|
|
93
103
|
}
|
|
104
|
+
|
|
94
105
|
return result;
|
|
95
106
|
}
|