@adecore/editor-react 0.31.12 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AnchoredPopup.js +73 -19
- package/dist/AttributionCard.js +162 -4
- package/dist/BaselineStatus.js +62 -8
- package/dist/CallHierarchyPanel.js +977 -60
- package/dist/ChangeReview.js +76 -25
- package/dist/CodeAuthorsCard.js +314 -14
- package/dist/CompletionPopup.js +490 -83
- package/dist/DiffView.js +548 -181
- package/dist/EditorContextMenu.js +352 -34
- package/dist/EditorView.js +276 -99
- package/dist/FindReplace.js +525 -67
- package/dist/HoverCard.js +378 -38
- package/dist/HoverSections.js +367 -56
- package/dist/LanguagePopups.js +95 -34
- package/dist/Markdown.js +41 -6
- package/dist/PanelResizeHandle.js +199 -66
- package/dist/PathText.js +49 -7
- package/dist/PeekPanel.js +675 -72
- package/dist/PickPopup.js +304 -34
- package/dist/RenameCard.js +679 -85
- package/dist/RenderedDocs.js +329 -58
- package/dist/SignatureCard.js +91 -7
- package/dist/SplitDiffView.js +344 -254
- package/dist/SymbolPicker.js +440 -85
- package/dist/call-hierarchy-model.js +102 -68
- package/dist/call-hierarchy.js +458 -359
- package/dist/code-actions-model.js +88 -76
- package/dist/code-actions.js +372 -283
- package/dist/code-authors.js +64 -55
- package/dist/code-vision-model.js +134 -127
- package/dist/code-vision.js +224 -206
- package/dist/completion-call.js +94 -67
- package/dist/completion-model.js +334 -244
- package/dist/completion.js +840 -649
- package/dist/conflict-model.js +35 -29
- package/dist/context-menu-rows.js +42 -17
- package/dist/context-menu.js +64 -46
- package/dist/definition-link.js +88 -85
- package/dist/diagnostics-model.js +74 -55
- package/dist/diagnostics.js +121 -100
- package/dist/diff-expansion.js +38 -35
- package/dist/diff-overlays.js +144 -22
- package/dist/diff-syntax.js +65 -42
- package/dist/diff-wordmarks.js +26 -23
- package/dist/doc-comment-model.js +265 -216
- package/dist/editor-language.js +177 -152
- package/dist/error-message.js +2 -2
- package/dist/find-query.js +49 -36
- package/dist/folding.js +81 -62
- package/dist/format-on-type.js +86 -67
- package/dist/highlight-layers.js +19 -20
- package/dist/highlights.js +105 -83
- package/dist/history.js +44 -38
- package/dist/holder.js +18 -18
- package/dist/host-types.js +1 -1
- package/dist/hover-content.js +167 -129
- package/dist/hover.js +285 -245
- package/dist/index.js +10 -10
- package/dist/inlay-hints.js +369 -324
- package/dist/key-priority.js +34 -31
- package/dist/language-ids.js +45 -42
- package/dist/line-edits.js +74 -28
- package/dist/line-text.js +18 -3
- package/dist/locales/en.json +38 -38
- package/dist/locales/nl.json +41 -41
- package/dist/models.js +1 -1
- package/dist/navigation-history.js +40 -41
- package/dist/navigation.js +141 -123
- package/dist/panel-size.js +8 -8
- package/dist/patch-decorations.js +236 -160
- package/dist/patch-layout.js +331 -262
- package/dist/paths.js +3 -3
- package/dist/peek-model.js +86 -69
- package/dist/peek.js +254 -192
- package/dist/pick.js +164 -157
- package/dist/popup-focus.js +5 -5
- package/dist/popup-placement.js +36 -21
- package/dist/popups.js +13 -13
- package/dist/project-language.js +228 -211
- package/dist/project-problems.js +81 -69
- package/dist/proposal-model.js +163 -126
- package/dist/recent-choices.js +25 -25
- package/dist/refresher.js +44 -49
- package/dist/rename-model.js +113 -113
- package/dist/rename.js +321 -233
- package/dist/rendered-docs.d.ts +4 -2
- package/dist/rendered-docs.js +384 -318
- package/dist/rendering-context.js +5 -3
- package/dist/rendering.js +22 -4
- package/dist/review-group.js +42 -39
- package/dist/row-host.d.ts +4 -2
- package/dist/row-host.js +90 -78
- package/dist/selection-ranges.js +21 -17
- package/dist/semantic-delta.js +22 -19
- package/dist/semantic-model.js +86 -73
- package/dist/semantic-tokens.js +201 -162
- package/dist/shortcut-keys.js +16 -10
- package/dist/signature-model.js +34 -28
- package/dist/signature.js +177 -129
- package/dist/snippet-field-transforms.js +118 -106
- package/dist/snippet-indent.js +48 -43
- package/dist/snippet-range-order.js +11 -11
- package/dist/snippet-session.js +703 -667
- package/dist/snippet-transform-client.js +59 -59
- package/dist/snippet-transform-format.js +285 -263
- package/dist/snippet-transform-protocol.js +1 -1
- package/dist/snippet-transform-queue.js +163 -153
- package/dist/snippet-transform-worker.js +42 -35
- package/dist/snippet-variable-names.js +1 -36
- package/dist/snippet-variables.js +53 -63
- package/dist/snippet.js +477 -405
- package/dist/split-alignment.js +47 -35
- package/dist/split-scroll.js +42 -40
- package/dist/symbol-links.js +33 -25
- package/dist/symbol-picker-model.js +126 -116
- package/dist/symbol-picker.js +98 -59
- package/dist/symbols.js +117 -88
- package/dist/testing.js +248 -165
- package/dist/timers.js +35 -30
- package/dist/use-panel-body.js +49 -8
- package/dist/use-problem-counts.js +29 -15
- package/dist/view-state.js +54 -33
- package/dist/word-diff.js +18 -18
- package/dist/workspace-edit.js +300 -249
- package/package.json +6 -6
- package/src/CallHierarchyPanel.tsx +2 -2
- package/src/DiffView.tsx +3 -3
- package/src/FindReplace.tsx +2 -2
- package/src/LanguagePopups.tsx +2 -0
- package/src/PeekPanel.tsx +3 -3
- package/src/RenderedDocs.tsx +9 -10
- package/src/SymbolPicker.tsx +2 -2
- package/src/diff-overlays.tsx +6 -4
- package/src/locales/en.json +38 -38
- package/src/locales/nl.json +41 -41
- package/src/rendered-docs.ts +14 -3
- package/src/row-host.ts +14 -3
package/dist/rendered-docs.js
CHANGED
|
@@ -12,28 +12,31 @@ const ESTIMATED_LINE_HEIGHT = 20;
|
|
|
12
12
|
const MAX_ESTIMATED_LINES = 8;
|
|
13
13
|
const MAX_PARSED = 200;
|
|
14
14
|
function startOf(comment) {
|
|
15
|
-
|
|
15
|
+
return {
|
|
16
|
+
line: comment.startLine,
|
|
17
|
+
character: comment.indent.length
|
|
18
|
+
};
|
|
16
19
|
}
|
|
17
20
|
/* A selection touches a comment unless it merely ends where the comment begins or swallows the comment whole. */
|
|
18
21
|
function touches(range, comment) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
if (range.end.line < comment.startLine || range.start.line > comment.endLine) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const empty = range.start.line === range.end.line && range.start.character === range.end.character;
|
|
26
|
+
if (!empty && range.end.line === comment.startLine && range.end.character === 0) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return empty || range.start.line >= comment.startLine || range.end.line <= comment.endLine;
|
|
27
30
|
}
|
|
28
31
|
/* The first words of what a comment says, to tell its row from the others for a person who hears it. */
|
|
29
32
|
function summaryOf(body) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
const lead = body.split('\n').findIndex(line => /^\s*@/.test(line));
|
|
34
|
+
const text = (lead === -1 ? body : body.split('\n').slice(0, lead).join('\n')).replace(/[`*_#>[\]()]/g, ' ');
|
|
35
|
+
const words = text.split(/\s+/).filter(word => word !== '');
|
|
36
|
+
return words.slice(0, SUMMARY_WORDS).join(' ');
|
|
34
37
|
}
|
|
35
38
|
function samePlace(range, position) {
|
|
36
|
-
|
|
39
|
+
return range !== null && range.start.line === position.line && range.start.character === position.character;
|
|
37
40
|
}
|
|
38
41
|
/*
|
|
39
42
|
* Documentation comments drawn as formatted text in place of their lines, the way the Reader mode of the
|
|
@@ -42,334 +45,397 @@ function samePlace(range, position) {
|
|
|
42
45
|
* it can be edited, and shown formatted again when the caret leaves it.
|
|
43
46
|
*/
|
|
44
47
|
export class RenderedDocsFeature {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const line = this.markerLines.get(id);
|
|
86
|
-
if (line !== undefined) {
|
|
87
|
-
this.toggleAt(line);
|
|
88
|
-
}
|
|
89
|
-
}),
|
|
90
|
-
language.onKey((event) => {
|
|
91
|
-
return isShortcut(this.language.shortcuts.toggleRenderedDocs, event) && this.toggle();
|
|
92
|
-
}, KEY_PRIORITY.renderedDocs),
|
|
93
|
-
editor.onViewChange(() => {
|
|
94
|
-
this.sweep();
|
|
95
|
-
this.releaseRevealed();
|
|
96
|
-
})
|
|
97
|
-
];
|
|
98
|
-
language.onDispose(() => {
|
|
99
|
-
this.disposed = true;
|
|
100
|
-
for (const off of offs) {
|
|
101
|
-
off();
|
|
102
|
-
}
|
|
103
|
-
this.timers.clear(this.settleTimer);
|
|
104
|
-
this.timers.clear(this.caretTimer);
|
|
105
|
-
for (const override of this.overrides) {
|
|
106
|
-
override.tracked.dispose();
|
|
107
|
-
}
|
|
108
|
-
this.overrides = [];
|
|
109
|
-
editor.setReplacements?.([], OWNER);
|
|
110
|
-
editor.setGutterMarkers([], OWNER);
|
|
111
|
-
this.containers.clear();
|
|
112
|
-
this.bump();
|
|
113
|
-
});
|
|
114
|
-
this.read();
|
|
115
|
-
}
|
|
116
|
-
/* Whether every documentation comment is drawn as text until a person switches it. Changing it puts every comment back under it. */
|
|
117
|
-
configure(settings) {
|
|
118
|
-
const enabled = settings.enabled !== false;
|
|
119
|
-
if (settings.renderDocComments === this.settings.renderDocComments && enabled === this.enabled) {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
const sameRendering = settings.renderDocComments === this.settings.renderDocComments;
|
|
123
|
-
this.settings = settings;
|
|
124
|
-
this.enabled = enabled;
|
|
125
|
-
if (!sameRendering) {
|
|
126
|
-
this.clearOverrides();
|
|
127
|
-
}
|
|
48
|
+
language;
|
|
49
|
+
timers;
|
|
50
|
+
settings = {
|
|
51
|
+
renderDocComments: false
|
|
52
|
+
};
|
|
53
|
+
enabled = true;
|
|
54
|
+
comments = [];
|
|
55
|
+
overrides = [];
|
|
56
|
+
containers = new Map();
|
|
57
|
+
markerLines = new Map();
|
|
58
|
+
parsed = new Map();
|
|
59
|
+
indexes = new Map();
|
|
60
|
+
/* 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. */
|
|
61
|
+
revealed = new Set();
|
|
62
|
+
listeners = new Set();
|
|
63
|
+
version = 0;
|
|
64
|
+
shown = null;
|
|
65
|
+
settleTimer;
|
|
66
|
+
caretTimer;
|
|
67
|
+
drawn = '';
|
|
68
|
+
marked = '';
|
|
69
|
+
/* The comments are from before the last edit, so nothing is decided from them until they are read again. */
|
|
70
|
+
stale = false;
|
|
71
|
+
disposed = false;
|
|
72
|
+
constructor(language, timers = realTimers) {
|
|
73
|
+
this.language = language;
|
|
74
|
+
this.timers = timers;
|
|
75
|
+
const {
|
|
76
|
+
editor
|
|
77
|
+
} = language;
|
|
78
|
+
const offs = [editor.onTextChange(() => {
|
|
79
|
+
this.stale = true;
|
|
80
|
+
this.timers.clear(this.settleTimer);
|
|
81
|
+
this.settleTimer = this.timers.set(() => this.read(), SETTLE_MS);
|
|
82
|
+
}),
|
|
83
|
+
// The caret is told before the text change that moved it, so what the lines are is decided a moment later.
|
|
84
|
+
editor.onCaret(() => {
|
|
85
|
+
this.timers.clear(this.caretTimer);
|
|
86
|
+
this.caretTimer = this.timers.set(() => {
|
|
87
|
+
this.revealed.clear();
|
|
128
88
|
this.publish();
|
|
89
|
+
}, 0);
|
|
90
|
+
}), editor.onGutterMarker(id => {
|
|
91
|
+
const line = this.markerLines.get(id);
|
|
92
|
+
if (line !== undefined) {
|
|
93
|
+
this.toggleAt(line);
|
|
94
|
+
}
|
|
95
|
+
}), language.onKey(event => {
|
|
96
|
+
return isShortcut(this.language.shortcuts.toggleRenderedDocs, event) && this.toggle();
|
|
97
|
+
}, KEY_PRIORITY.renderedDocs), editor.onViewChange(() => {
|
|
98
|
+
this.sweep();
|
|
99
|
+
this.releaseRevealed();
|
|
100
|
+
})];
|
|
101
|
+
language.onDispose(() => {
|
|
102
|
+
this.disposed = true;
|
|
103
|
+
for (const off of offs) {
|
|
104
|
+
off();
|
|
105
|
+
}
|
|
106
|
+
this.timers.clear(this.settleTimer);
|
|
107
|
+
this.timers.clear(this.caretTimer);
|
|
108
|
+
for (const override of this.overrides) {
|
|
109
|
+
override.tracked.dispose();
|
|
110
|
+
}
|
|
111
|
+
this.overrides = [];
|
|
112
|
+
editor.setReplacements?.([], OWNER);
|
|
113
|
+
editor.setGutterMarkers([], OWNER);
|
|
114
|
+
this.containers.clear();
|
|
115
|
+
this.bump();
|
|
116
|
+
});
|
|
117
|
+
this.read();
|
|
118
|
+
}
|
|
119
|
+
/* Whether every documentation comment is drawn as text until a person switches it. Changing it puts every comment back under it. */
|
|
120
|
+
configure(settings) {
|
|
121
|
+
const enabled = settings.enabled !== false;
|
|
122
|
+
if (settings.renderDocComments === this.settings.renderDocComments && enabled === this.enabled) {
|
|
123
|
+
return;
|
|
129
124
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
125
|
+
const sameRendering = settings.renderDocComments === this.settings.renderDocComments;
|
|
126
|
+
this.settings = settings;
|
|
127
|
+
this.enabled = enabled;
|
|
128
|
+
if (!sameRendering) {
|
|
129
|
+
this.clearOverrides();
|
|
133
130
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
131
|
+
this.publish();
|
|
132
|
+
}
|
|
133
|
+
/* The documentation comments of the document, from the last time it settled. */
|
|
134
|
+
get all() {
|
|
135
|
+
return this.comments;
|
|
136
|
+
}
|
|
137
|
+
/* Whether a comment is drawn as text now: its own switch, else the setting. A comment the caret is in is not, whatever its switch says. */
|
|
138
|
+
isRendered(comment) {
|
|
139
|
+
return this.wantsRendered(comment) && !this.touched(comment) && !this.revealed.has(comment);
|
|
140
|
+
}
|
|
141
|
+
/* Switches the comment the caret is in between formatted and source. False when the caret is in none. */
|
|
142
|
+
toggle() {
|
|
143
|
+
this.settle();
|
|
144
|
+
const comment = this.at(this.language.editor.getCaret().line);
|
|
145
|
+
if (!this.enabled || comment === undefined || !this.canRender(comment)) {
|
|
146
|
+
return false;
|
|
137
147
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
148
|
+
this.setRendered(comment, !this.wantsRendered(comment));
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
/* Switches the comment that starts on a line, which is what its gutter button does. */
|
|
152
|
+
toggleAt(startLine) {
|
|
153
|
+
this.settle();
|
|
154
|
+
const comment = this.comments.find(candidate => candidate.startLine === startLine);
|
|
155
|
+
if (!this.enabled || comment === undefined || !this.canRender(comment)) {
|
|
156
|
+
return false;
|
|
147
157
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
158
|
+
this.setRendered(comment, !this.wantsRendered(comment));
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
/* The elements of the rows now in view, for a host that draws them itself; `RenderedDocs` does. */
|
|
162
|
+
entries() {
|
|
163
|
+
const result = [];
|
|
164
|
+
for (const [id, container] of this.containers) {
|
|
165
|
+
const comment = this.comments[Number(id.slice(DOCS_PREFIX.length))];
|
|
166
|
+
if (comment !== undefined) {
|
|
167
|
+
result.push({
|
|
168
|
+
id,
|
|
169
|
+
comment,
|
|
170
|
+
parsed: this.parse(comment),
|
|
171
|
+
container
|
|
172
|
+
});
|
|
173
|
+
}
|
|
157
174
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
getVersion() {
|
|
178
|
+
return this.version;
|
|
179
|
+
}
|
|
180
|
+
/* What `entries` answers, one array until the version moves, so React can read it as a snapshot. */
|
|
181
|
+
getEntries() {
|
|
182
|
+
if (this.shown?.version !== this.version) {
|
|
183
|
+
this.shown = {
|
|
184
|
+
version: this.version,
|
|
185
|
+
entries: this.entries()
|
|
186
|
+
};
|
|
168
187
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
188
|
+
return this.shown.entries;
|
|
189
|
+
}
|
|
190
|
+
subscribe(listener) {
|
|
191
|
+
this.listeners.add(listener);
|
|
192
|
+
return () => {
|
|
193
|
+
this.listeners.delete(listener);
|
|
175
194
|
};
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
195
|
+
}
|
|
196
|
+
/* Whether names in `{@link}` and `@see` can be followed, which takes a server that finds symbols by name. */
|
|
197
|
+
get canFollow() {
|
|
198
|
+
const {
|
|
199
|
+
project,
|
|
200
|
+
uri
|
|
201
|
+
} = this.language;
|
|
202
|
+
return project.service.supports('workspace/symbol', uri);
|
|
203
|
+
}
|
|
204
|
+
/* Goes to what a link in a comment names. */
|
|
205
|
+
follow(name, comment) {
|
|
206
|
+
const member = name.split(/::|[#.\\]/).filter(part => part !== '');
|
|
207
|
+
const target = (member.at(-1) ?? name).replace(/\(\)$/, '');
|
|
208
|
+
void this.language.navigation.goToName(target, startOf(comment), null);
|
|
209
|
+
}
|
|
210
|
+
/* By place in the document, so a row keeps its element when an edit above it moves its lines. */
|
|
211
|
+
idOf(comment) {
|
|
212
|
+
return `${DOCS_PREFIX}${this.indexes.get(comment) ?? -1}`;
|
|
213
|
+
}
|
|
214
|
+
/* A comment that is all the document has has no line to leave the caret on, so it stays source. */
|
|
215
|
+
canRender(comment) {
|
|
216
|
+
return comment.startLine > 0 || comment.endLine < this.language.editor.positionAt(Number.MAX_SAFE_INTEGER).line;
|
|
217
|
+
}
|
|
218
|
+
wantsRendered(comment) {
|
|
219
|
+
if (!this.enabled || !this.canRender(comment)) {
|
|
220
|
+
return false;
|
|
186
221
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
222
|
+
const start = startOf(comment);
|
|
223
|
+
const override = this.overrides.find(candidate => samePlace(candidate.tracked.get(), start));
|
|
224
|
+
return override?.rendered ?? this.settings.renderDocComments;
|
|
225
|
+
}
|
|
226
|
+
at(line) {
|
|
227
|
+
return this.comments.find(comment => line >= comment.startLine && line <= comment.endLine);
|
|
228
|
+
}
|
|
229
|
+
touched(comment) {
|
|
230
|
+
return this.language.editor.getSelections().some(range => touches(range, comment));
|
|
231
|
+
}
|
|
232
|
+
setRendered(comment, rendered) {
|
|
233
|
+
const start = startOf(comment);
|
|
234
|
+
const existing = this.overrides.find(candidate => samePlace(candidate.tracked.get(), start));
|
|
235
|
+
if (existing !== undefined) {
|
|
236
|
+
existing.tracked.dispose();
|
|
237
|
+
this.overrides = this.overrides.filter(candidate => candidate !== existing);
|
|
190
238
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
239
|
+
if (rendered !== this.settings.renderDocComments) {
|
|
240
|
+
const {
|
|
241
|
+
editor
|
|
242
|
+
} = this.language;
|
|
243
|
+
this.overrides.push({
|
|
244
|
+
tracked: editor.trackRange({
|
|
245
|
+
start,
|
|
246
|
+
end: editor.positionAt(comment.to)
|
|
247
|
+
}),
|
|
248
|
+
rendered
|
|
249
|
+
});
|
|
194
250
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
return false;
|
|
198
|
-
}
|
|
199
|
-
const start = startOf(comment);
|
|
200
|
-
const override = this.overrides.find((candidate) => samePlace(candidate.tracked.get(), start));
|
|
201
|
-
return override?.rendered ?? this.settings.renderDocComments;
|
|
251
|
+
if (rendered) {
|
|
252
|
+
this.leave(comment);
|
|
202
253
|
}
|
|
203
|
-
|
|
204
|
-
|
|
254
|
+
this.publish();
|
|
255
|
+
}
|
|
256
|
+
/* Takes the carets and selections that touch a comment about to be drawn as text to the line under it, else the one above it. The others stay. */
|
|
257
|
+
leave(comment) {
|
|
258
|
+
const {
|
|
259
|
+
editor
|
|
260
|
+
} = this.language;
|
|
261
|
+
const selections = editor.getSelections();
|
|
262
|
+
if (!selections.some(range => touches(range, comment))) {
|
|
263
|
+
return;
|
|
205
264
|
}
|
|
206
|
-
|
|
207
|
-
|
|
265
|
+
const count = editor.positionAt(Number.MAX_SAFE_INTEGER).line + 1;
|
|
266
|
+
const line = comment.endLine + 1 < count ? comment.endLine + 1 : comment.startLine - 1;
|
|
267
|
+
if (line < 0) {
|
|
268
|
+
return;
|
|
208
269
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const text = editor.textInRange({ start: { line, character: 0 }, end: { line, character: Number.MAX_SAFE_INTEGER } });
|
|
238
|
-
const character = line > comment.endLine ? (/^[ \t]*/.exec(text)?.[0].length ?? 0) : text.length;
|
|
239
|
-
const place = { line, character };
|
|
240
|
-
editor.setSelections(selections.map((range) => (touches(range, comment) ? { start: place, end: place } : range)));
|
|
270
|
+
const text = editor.textInRange({
|
|
271
|
+
start: {
|
|
272
|
+
line,
|
|
273
|
+
character: 0
|
|
274
|
+
},
|
|
275
|
+
end: {
|
|
276
|
+
line,
|
|
277
|
+
character: Number.MAX_SAFE_INTEGER
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
const character = line > comment.endLine ? /^[ \t]*/.exec(text)?.[0].length ?? 0 : text.length;
|
|
281
|
+
const place = {
|
|
282
|
+
line,
|
|
283
|
+
character
|
|
284
|
+
};
|
|
285
|
+
editor.setSelections(selections.map(range => touches(range, comment) ? {
|
|
286
|
+
start: place,
|
|
287
|
+
end: place
|
|
288
|
+
} : range));
|
|
289
|
+
}
|
|
290
|
+
/* Shows a comment as source for the reveal that is about to scroll into it, since its row would only be in the way. */
|
|
291
|
+
reveal(comment) {
|
|
292
|
+
this.revealed.add(comment);
|
|
293
|
+
this.publish();
|
|
294
|
+
}
|
|
295
|
+
releaseRevealed() {
|
|
296
|
+
if (this.revealed.size === 0) {
|
|
297
|
+
return;
|
|
241
298
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
299
|
+
const {
|
|
300
|
+
start,
|
|
301
|
+
end
|
|
302
|
+
} = this.language.editor.getVisibleRange();
|
|
303
|
+
const away = [...this.revealed].filter(comment => comment.endLine < start.line || comment.startLine > end.line);
|
|
304
|
+
if (away.length > 0) {
|
|
305
|
+
away.forEach(comment => this.revealed.delete(comment));
|
|
306
|
+
this.publish();
|
|
246
307
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
const { start, end } = this.language.editor.getVisibleRange();
|
|
252
|
-
const away = [...this.revealed].filter((comment) => comment.endLine < start.line || comment.startLine > end.line);
|
|
253
|
-
if (away.length > 0) {
|
|
254
|
-
away.forEach((comment) => this.revealed.delete(comment));
|
|
255
|
-
this.publish();
|
|
256
|
-
}
|
|
308
|
+
}
|
|
309
|
+
clearOverrides() {
|
|
310
|
+
for (const override of this.overrides) {
|
|
311
|
+
override.tracked.dispose();
|
|
257
312
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
313
|
+
this.overrides = [];
|
|
314
|
+
}
|
|
315
|
+
/* Reads the comments now when an edit is waiting to be read. */
|
|
316
|
+
settle() {
|
|
317
|
+
if (this.stale) {
|
|
318
|
+
this.timers.clear(this.settleTimer);
|
|
319
|
+
this.read();
|
|
263
320
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
this.read();
|
|
269
|
-
}
|
|
321
|
+
}
|
|
322
|
+
read() {
|
|
323
|
+
if (this.disposed) {
|
|
324
|
+
return;
|
|
270
325
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
326
|
+
this.stale = false;
|
|
327
|
+
const {
|
|
328
|
+
editor,
|
|
329
|
+
languageId
|
|
330
|
+
} = this.language;
|
|
331
|
+
this.comments = findDocComments(editor.getText(), languageId);
|
|
332
|
+
this.indexes = new Map(this.comments.map((comment, index) => [comment, index]));
|
|
333
|
+
this.revealed.clear();
|
|
334
|
+
const starts = this.comments.map(startOf);
|
|
335
|
+
this.overrides = this.overrides.filter(override => {
|
|
336
|
+
const kept = starts.some(start => samePlace(override.tracked.get(), start));
|
|
337
|
+
if (!kept) {
|
|
338
|
+
override.tracked.dispose();
|
|
339
|
+
}
|
|
340
|
+
return kept;
|
|
341
|
+
});
|
|
342
|
+
// An edit that replaced everything leaves the rows on offsets at the end, which only a new set of rows repairs.
|
|
343
|
+
this.publish(true);
|
|
344
|
+
this.bump();
|
|
345
|
+
}
|
|
346
|
+
parse(comment) {
|
|
347
|
+
const key = `${comment.body}`;
|
|
348
|
+
let result = this.parsed.get(key);
|
|
349
|
+
if (result === undefined) {
|
|
350
|
+
if (this.parsed.size >= MAX_PARSED) {
|
|
351
|
+
this.parsed.delete(this.parsed.keys().next().value);
|
|
352
|
+
}
|
|
353
|
+
result = parseDocComment(comment.body, {
|
|
354
|
+
php: /^php$/i.test(this.language.languageId)
|
|
355
|
+
});
|
|
356
|
+
this.parsed.set(key, result);
|
|
291
357
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
299
|
-
result = parseDocComment(comment.body, { php: /^php$/i.test(this.language.languageId) });
|
|
300
|
-
this.parsed.set(key, result);
|
|
301
|
-
}
|
|
302
|
-
return result;
|
|
358
|
+
return result;
|
|
359
|
+
}
|
|
360
|
+
/* Hands the editor the rows and the buttons, unless they are what it has already or `force` says to. */
|
|
361
|
+
publish(force = false) {
|
|
362
|
+
if (this.disposed || this.stale) {
|
|
363
|
+
return;
|
|
303
364
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
for (const id of [...this.containers.keys()]) {
|
|
327
|
-
if (!kept.has(id)) {
|
|
328
|
-
this.containers.delete(id);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
editor.setReplacements?.(rows, OWNER);
|
|
332
|
-
this.bump();
|
|
333
|
-
}
|
|
334
|
-
this.markerLines.clear();
|
|
335
|
-
const markers = (this.enabled ? this.comments : []).map((comment) => {
|
|
336
|
-
const id = `docs:${comment.startLine}`;
|
|
337
|
-
this.markerLines.set(id, comment.startLine);
|
|
338
|
-
const on = this.wantsRendered(comment);
|
|
339
|
-
return { id, line: comment.startLine, label: t(on ? 'showSource' : 'render'), icon: 'book-open', pressed: on };
|
|
340
|
-
});
|
|
341
|
-
const marking = markers.map((marker) => `${marker.line}${marker.pressed}`).join('|');
|
|
342
|
-
if (marking !== this.marked) {
|
|
343
|
-
this.marked = marking;
|
|
344
|
-
editor.setGutterMarkers(markers, OWNER);
|
|
365
|
+
const {
|
|
366
|
+
editor
|
|
367
|
+
} = this.language;
|
|
368
|
+
const t = (key, options) => this.language.project.i18n.t(`editor:language.docs.${key}`, options);
|
|
369
|
+
const rendered = this.comments.filter(comment => this.isRendered(comment));
|
|
370
|
+
const signature = rendered.map(comment => `${this.idOf(comment)}@${comment.startLine}-${comment.endLine}`).join('|');
|
|
371
|
+
if (force || signature !== this.drawn) {
|
|
372
|
+
this.drawn = signature;
|
|
373
|
+
const lineHeight = Math.max(editor.getBaseLineHeight?.() ?? 0, ESTIMATED_LINE_HEIGHT);
|
|
374
|
+
const rows = rendered.map(comment => ({
|
|
375
|
+
id: this.idOf(comment),
|
|
376
|
+
startLine: comment.startLine,
|
|
377
|
+
endLine: comment.endLine,
|
|
378
|
+
height: Math.min(comment.endLine - comment.startLine + 1, MAX_ESTIMATED_LINES) * lineHeight,
|
|
379
|
+
label: this.labelOf(comment, t),
|
|
380
|
+
onReveal: () => this.reveal(comment),
|
|
381
|
+
render: container => this.mounted(this.idOf(comment), container)
|
|
382
|
+
}));
|
|
383
|
+
const kept = new Set(rows.map(row => row.id));
|
|
384
|
+
for (const id of [...this.containers.keys()]) {
|
|
385
|
+
if (!kept.has(id)) {
|
|
386
|
+
this.containers.delete(id);
|
|
345
387
|
}
|
|
388
|
+
}
|
|
389
|
+
editor.setReplacements?.(rows, OWNER);
|
|
390
|
+
this.bump();
|
|
346
391
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
392
|
+
this.markerLines.clear();
|
|
393
|
+
const markers = (this.enabled ? this.comments : []).map(comment => {
|
|
394
|
+
const id = `docs:${comment.startLine}`;
|
|
395
|
+
this.markerLines.set(id, comment.startLine);
|
|
396
|
+
const on = this.wantsRendered(comment);
|
|
397
|
+
return {
|
|
398
|
+
id,
|
|
399
|
+
line: comment.startLine,
|
|
400
|
+
label: t(on ? 'showSource' : 'render'),
|
|
401
|
+
icon: 'book-open',
|
|
402
|
+
pressed: on
|
|
403
|
+
};
|
|
404
|
+
});
|
|
405
|
+
const marking = markers.map(marker => `${marker.line}${marker.pressed}`).join('|');
|
|
406
|
+
if (marking !== this.marked) {
|
|
407
|
+
this.marked = marking;
|
|
408
|
+
editor.setGutterMarkers(markers, OWNER);
|
|
350
409
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
410
|
+
}
|
|
411
|
+
labelOf(comment, t) {
|
|
412
|
+
const summary = summaryOf(comment.body);
|
|
413
|
+
return summary === '' ? t('label') : t('labelNamed', {
|
|
414
|
+
summary
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
mounted(id, container) {
|
|
418
|
+
this.containers.set(id, container);
|
|
419
|
+
this.sweep(container);
|
|
420
|
+
this.bump();
|
|
421
|
+
}
|
|
422
|
+
/* 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
|
+
sweep(fresh) {
|
|
424
|
+
let removed = false;
|
|
425
|
+
for (const [id, container] of this.containers) {
|
|
426
|
+
if (container !== fresh && !container.isConnected) {
|
|
427
|
+
this.containers.delete(id);
|
|
428
|
+
removed = true;
|
|
429
|
+
}
|
|
355
430
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
let removed = false;
|
|
359
|
-
for (const [id, container] of this.containers) {
|
|
360
|
-
if (container !== fresh && !container.isConnected) {
|
|
361
|
-
this.containers.delete(id);
|
|
362
|
-
removed = true;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
if (removed && fresh === undefined) {
|
|
366
|
-
this.bump();
|
|
367
|
-
}
|
|
431
|
+
if (removed && fresh === undefined) {
|
|
432
|
+
this.bump();
|
|
368
433
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
434
|
+
}
|
|
435
|
+
bump() {
|
|
436
|
+
this.version++;
|
|
437
|
+
for (const listener of [...this.listeners]) {
|
|
438
|
+
listener();
|
|
374
439
|
}
|
|
375
|
-
}
|
|
440
|
+
}
|
|
441
|
+
}
|