@adecore/editor-react 0.22.1 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CallHierarchyPanel.d.ts +6 -0
- package/dist/CallHierarchyPanel.js +92 -0
- package/dist/CompletionPopup.js +11 -3
- package/dist/DiffView.d.ts +22 -0
- package/dist/DiffView.js +203 -0
- package/dist/EditorContextMenu.js +1 -0
- package/dist/EditorView.js +18 -1
- package/dist/FindReplace.d.ts +1 -1
- package/dist/FindReplace.js +41 -11
- package/dist/HoverCard.js +2 -2
- package/dist/LanguagePopups.js +4 -1
- package/dist/Markdown.d.ts +2 -1
- package/dist/Markdown.js +8 -4
- package/dist/PanelResizeHandle.d.ts +8 -0
- package/dist/PanelResizeHandle.js +71 -0
- package/dist/PeekPanel.d.ts +3 -0
- package/dist/PeekPanel.js +7 -4
- package/dist/PickPopup.js +15 -2
- package/dist/RenameCard.js +10 -5
- package/dist/RenderedDocs.d.ts +9 -0
- package/dist/RenderedDocs.js +84 -0
- package/dist/SplitDiffView.d.ts +9 -0
- package/dist/SplitDiffView.js +276 -0
- package/dist/SymbolPicker.d.ts +3 -0
- package/dist/SymbolPicker.js +4 -3
- package/dist/call-hierarchy-model.d.ts +52 -0
- package/dist/call-hierarchy-model.js +93 -0
- package/dist/call-hierarchy.d.ts +42 -0
- package/dist/call-hierarchy.js +398 -0
- package/dist/code-actions-model.d.ts +2 -0
- package/dist/code-actions-model.js +12 -10
- package/dist/code-actions.js +3 -1
- package/dist/completion-model.d.ts +14 -4
- package/dist/completion-model.js +40 -33
- package/dist/completion.d.ts +4 -0
- package/dist/completion.js +257 -42
- package/dist/diagnostics.js +8 -6
- package/dist/diff-expansion.d.ts +9 -0
- package/dist/diff-expansion.js +41 -0
- package/dist/diff-overlays.d.ts +15 -0
- package/dist/diff-overlays.js +28 -0
- package/dist/diff-syntax.d.ts +16 -0
- package/dist/diff-syntax.js +49 -0
- package/dist/diff-wordmarks.d.ts +5 -0
- package/dist/diff-wordmarks.js +26 -0
- package/dist/doc-comment-model.d.ts +23 -0
- package/dist/doc-comment-model.js +291 -0
- package/dist/editor-language.d.ts +13 -1
- package/dist/editor-language.js +15 -5
- package/dist/editor-react.css +79 -0
- package/dist/format-on-type.d.ts +14 -0
- package/dist/format-on-type.js +96 -0
- package/dist/host-types.d.ts +19 -0
- package/dist/hover.d.ts +4 -0
- package/dist/hover.js +15 -2
- package/dist/index.d.ts +19 -5
- package/dist/index.js +9 -1
- package/dist/inlay-hints.d.ts +47 -2
- package/dist/inlay-hints.js +353 -16
- package/dist/language-ids.js +4 -2
- package/dist/locales/en.json +117 -3
- package/dist/locales/nl.json +117 -3
- package/dist/models.d.ts +6 -2
- package/dist/models.js +5 -1
- package/dist/panel-size.d.ts +6 -0
- package/dist/panel-size.js +25 -0
- package/dist/patch-decorations.d.ts +27 -0
- package/dist/patch-decorations.js +182 -0
- package/dist/patch-layout.d.ts +40 -0
- package/dist/patch-layout.js +304 -0
- package/dist/peek.d.ts +3 -1
- package/dist/peek.js +6 -1
- package/dist/pick.d.ts +1 -0
- package/dist/pick.js +11 -1
- package/dist/popups.d.ts +17 -0
- package/dist/popups.js +1 -0
- package/dist/project-language.d.ts +12 -0
- package/dist/project-language.js +52 -2
- package/dist/refresher.d.ts +1 -0
- package/dist/refresher.js +5 -0
- package/dist/rename-model.d.ts +1 -1
- package/dist/rename-model.js +3 -7
- package/dist/rename.js +5 -8
- package/dist/rendered-docs.d.ts +64 -0
- package/dist/rendered-docs.js +374 -0
- package/dist/row-host.d.ts +2 -0
- package/dist/row-host.js +11 -3
- package/dist/semantic-delta.d.ts +2 -0
- package/dist/semantic-delta.js +27 -0
- package/dist/semantic-model.js +16 -0
- package/dist/semantic-tokens.d.ts +26 -1
- package/dist/semantic-tokens.js +174 -14
- package/dist/shortcut-keys.d.ts +2 -0
- package/dist/shortcut-keys.js +6 -2
- package/dist/signature.js +1 -1
- package/dist/snippet-field-transforms.d.ts +30 -0
- package/dist/snippet-field-transforms.js +116 -0
- package/dist/snippet-indent.d.ts +2 -0
- package/dist/snippet-indent.js +49 -0
- package/dist/snippet-range-order.d.ts +3 -0
- package/dist/snippet-range-order.js +19 -0
- package/dist/snippet-session.d.ts +42 -6
- package/dist/snippet-session.js +650 -53
- package/dist/snippet-transform-client.d.ts +19 -0
- package/dist/snippet-transform-client.js +66 -0
- package/dist/snippet-transform-format.d.ts +15 -0
- package/dist/snippet-transform-format.js +302 -0
- package/dist/snippet-transform-protocol.d.ts +45 -0
- package/dist/snippet-transform-protocol.js +8 -0
- package/dist/snippet-transform-queue.d.ts +32 -0
- package/dist/snippet-transform-queue.js +173 -0
- package/dist/snippet-transform-worker.d.ts +10 -0
- package/dist/snippet-transform-worker.js +41 -0
- package/dist/snippet-variable-names.d.ts +1 -0
- package/dist/snippet-variable-names.js +36 -0
- package/dist/snippet-variables.d.ts +7 -0
- package/dist/snippet-variables.js +66 -0
- package/dist/snippet.d.ts +30 -3
- package/dist/snippet.js +402 -87
- package/dist/split-alignment.d.ts +6 -0
- package/dist/split-alignment.js +38 -0
- package/dist/split-scroll.d.ts +14 -0
- package/dist/split-scroll.js +45 -0
- package/dist/symbols.d.ts +2 -1
- package/dist/symbols.js +39 -7
- package/dist/testing.d.ts +5 -1
- package/dist/testing.js +12 -0
- package/dist/timers.d.ts +1 -0
- package/dist/timers.js +4 -0
- package/dist/use-panel-body.d.ts +6 -0
- package/dist/use-panel-body.js +13 -0
- package/dist/workspace-edit.d.ts +25 -3
- package/dist/workspace-edit.js +282 -143
- package/package.json +10 -5
- package/src/CallHierarchyPanel.tsx +244 -0
- package/src/CompletionPopup.tsx +12 -1
- package/src/DiffView.tsx +262 -0
- package/src/EditorContextMenu.tsx +1 -0
- package/src/EditorView.tsx +21 -2
- package/src/FindReplace.tsx +51 -13
- package/src/HoverCard.tsx +23 -21
- package/src/LanguagePopups.tsx +5 -0
- package/src/Markdown.tsx +10 -3
- package/src/PanelResizeHandle.tsx +104 -0
- package/src/PeekPanel.tsx +11 -6
- package/src/PickPopup.tsx +18 -2
- package/src/RenameCard.tsx +18 -0
- package/src/RenderedDocs.tsx +155 -0
- package/src/SplitDiffView.tsx +337 -0
- package/src/SymbolPicker.tsx +4 -3
- package/src/call-hierarchy-model.ts +151 -0
- package/src/call-hierarchy.ts +440 -0
- package/src/code-actions-model.ts +17 -11
- package/src/code-actions.ts +3 -1
- package/src/completion-model.ts +61 -35
- package/src/completion.ts +302 -42
- package/src/diagnostics.ts +8 -6
- package/src/diff-expansion.ts +49 -0
- package/src/diff-overlays.tsx +69 -0
- package/src/diff-syntax.ts +67 -0
- package/src/diff-wordmarks.ts +31 -0
- package/src/doc-comment-model.ts +336 -0
- package/src/editor-language.ts +22 -6
- package/src/editor-react.css +79 -0
- package/src/format-on-type.ts +111 -0
- package/src/host-types.ts +23 -0
- package/src/hover.ts +18 -3
- package/src/index.ts +27 -4
- package/src/inlay-hints.ts +394 -18
- package/src/language-ids.ts +4 -2
- package/src/locales/en.json +117 -3
- package/src/locales/nl.json +117 -3
- package/src/models.ts +40 -1
- package/src/panel-size.ts +28 -0
- package/src/patch-decorations.ts +224 -0
- package/src/patch-layout.ts +388 -0
- package/src/peek.ts +6 -1
- package/src/pick.ts +12 -1
- package/src/popups.ts +24 -0
- package/src/project-language.ts +63 -2
- package/src/refresher.ts +6 -0
- package/src/rename-model.ts +4 -8
- package/src/rename.ts +5 -8
- package/src/rendered-docs.ts +430 -0
- package/src/row-host.ts +12 -3
- package/src/semantic-delta.ts +29 -0
- package/src/semantic-model.ts +17 -0
- package/src/semantic-tokens.ts +202 -19
- package/src/shortcut-keys.ts +6 -2
- package/src/signature.ts +1 -1
- package/src/snippet-field-transforms.ts +144 -0
- package/src/snippet-indent.ts +53 -0
- package/src/snippet-range-order.ts +19 -0
- package/src/snippet-session.ts +797 -56
- package/src/snippet-transform-client.ts +84 -0
- package/src/snippet-transform-format.ts +325 -0
- package/src/snippet-transform-protocol.ts +24 -0
- package/src/snippet-transform-queue.ts +199 -0
- package/src/snippet-transform-worker.ts +58 -0
- package/src/snippet-variable-names.ts +36 -0
- package/src/snippet-variables.ts +74 -0
- package/src/snippet.ts +452 -89
- package/src/split-alignment.ts +45 -0
- package/src/split-scroll.ts +54 -0
- package/src/symbols.ts +43 -7
- package/src/testing.ts +25 -0
- package/src/timers.ts +5 -0
- package/src/use-panel-body.ts +17 -0
- package/src/workspace-edit.ts +347 -169
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { parseHunkHeader } from '@adecore/merge';
|
|
2
|
+
export const DEFAULT_PATCH_LIMITS = { maxLines: 50_000, maxCharacters: 4_000_000 };
|
|
3
|
+
const OTHER = { role: 'other', old: null, new: null };
|
|
4
|
+
const FILE_HEADER = { role: 'file-header', old: null, new: null };
|
|
5
|
+
const HUNK_HEADER = { role: 'hunk-header', old: null, new: null };
|
|
6
|
+
const NO_NEWLINE = { role: 'no-newline', old: null, new: null };
|
|
7
|
+
const BINARY = { role: 'binary', old: null, new: null };
|
|
8
|
+
const GIT_HEADERS = /^(?:index |old mode |new mode |deleted file mode |new file mode |similarity index |dissimilarity index |rename from |rename to |copy from |copy to )/;
|
|
9
|
+
const ESCAPES = { a: 7, b: 8, f: 12, n: 10, r: 13, t: 9, v: 11, '"': 34, '\\': 92 };
|
|
10
|
+
/* Reads a path as git writes it: bare, or in quotes with C escapes and octal bytes. */
|
|
11
|
+
function unquote(raw) {
|
|
12
|
+
if (raw.length < 2 || !raw.startsWith('"') || !raw.endsWith('"')) {
|
|
13
|
+
return raw;
|
|
14
|
+
}
|
|
15
|
+
const bytes = [];
|
|
16
|
+
const encoder = new TextEncoder();
|
|
17
|
+
const body = raw.slice(1, -1);
|
|
18
|
+
for (let i = 0; i < body.length; i++) {
|
|
19
|
+
if (body[i] !== '\\') {
|
|
20
|
+
const point = String.fromCodePoint(body.codePointAt(i));
|
|
21
|
+
bytes.push(...encoder.encode(point));
|
|
22
|
+
i += point.length - 1;
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const octal = /^[0-3][0-7]{2}/.exec(body.slice(i + 1, i + 4));
|
|
26
|
+
const escaped = ESCAPES[body[i + 1] ?? ''];
|
|
27
|
+
if (octal !== null) {
|
|
28
|
+
bytes.push(Number.parseInt(octal[0], 8));
|
|
29
|
+
i += 3;
|
|
30
|
+
}
|
|
31
|
+
else if (escaped !== undefined) {
|
|
32
|
+
bytes.push(escaped);
|
|
33
|
+
i++;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
bytes.push(92);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return new TextDecoder().decode(new Uint8Array(bytes));
|
|
40
|
+
}
|
|
41
|
+
/* The name on a `---`, `+++`, `rename` or `copy` line: a timestamp after a tab goes, and `/dev/null` is no file. */
|
|
42
|
+
function pathOf(raw) {
|
|
43
|
+
const tab = raw.indexOf('\t');
|
|
44
|
+
const name = unquote(tab === -1 ? raw : raw.slice(0, tab));
|
|
45
|
+
return name === '/dev/null' ? null : name;
|
|
46
|
+
}
|
|
47
|
+
function stripPrefix(path, prefix) {
|
|
48
|
+
return path !== null && path.startsWith(prefix) ? path.slice(2) : path;
|
|
49
|
+
}
|
|
50
|
+
/* The two names of a `diff --git a/x b/y` line; with spaces in an unquoted name, the split that gives both halves the same name wins. */
|
|
51
|
+
function gitPaths(rest) {
|
|
52
|
+
if (rest.startsWith('"')) {
|
|
53
|
+
const end = rest.indexOf('" ', 1);
|
|
54
|
+
if (end !== -1) {
|
|
55
|
+
const oldName = unquote(rest.slice(0, end + 1));
|
|
56
|
+
const newName = unquote(rest.slice(end + 2));
|
|
57
|
+
const prefixed = oldName.startsWith('a/') && newName.startsWith('b/');
|
|
58
|
+
return prefixed ? { oldPath: oldName.slice(2), newPath: newName.slice(2), prefixed } : { oldPath: oldName, newPath: newName, prefixed };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const middle = rest.indexOf(' b/');
|
|
62
|
+
if (rest.startsWith('a/') && middle !== -1) {
|
|
63
|
+
for (let at = middle; at !== -1; at = rest.indexOf(' b/', at + 1)) {
|
|
64
|
+
if (rest.slice(2, at) === rest.slice(at + 3)) {
|
|
65
|
+
return { oldPath: rest.slice(2, at), newPath: rest.slice(at + 3), prefixed: true };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { oldPath: rest.slice(2, middle), newPath: unquote(rest.slice(middle + 3)), prefixed: true };
|
|
69
|
+
}
|
|
70
|
+
const split = rest.indexOf(' ');
|
|
71
|
+
return split === -1
|
|
72
|
+
? { oldPath: unquote(rest), newPath: null, prefixed: false }
|
|
73
|
+
: { oldPath: unquote(rest.slice(0, split)), newPath: unquote(rest.slice(split + 1)), prefixed: false };
|
|
74
|
+
}
|
|
75
|
+
function emptySection(firstLine, git) {
|
|
76
|
+
return {
|
|
77
|
+
oldPath: null,
|
|
78
|
+
newPath: null,
|
|
79
|
+
git,
|
|
80
|
+
created: false,
|
|
81
|
+
deleted: false,
|
|
82
|
+
renamed: false,
|
|
83
|
+
copied: false,
|
|
84
|
+
binary: false,
|
|
85
|
+
firstLine,
|
|
86
|
+
lastLine: firstLine,
|
|
87
|
+
added: 0,
|
|
88
|
+
removed: 0,
|
|
89
|
+
hunks: [],
|
|
90
|
+
named: false,
|
|
91
|
+
prefixed: false
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function statusOf(section) {
|
|
95
|
+
if (section.binary) {
|
|
96
|
+
return 'binary';
|
|
97
|
+
}
|
|
98
|
+
if (section.created || (!section.git && section.oldPath === null && section.newPath !== null)) {
|
|
99
|
+
return 'added';
|
|
100
|
+
}
|
|
101
|
+
if (section.deleted || (!section.git && section.newPath === null && section.oldPath !== null)) {
|
|
102
|
+
return 'deleted';
|
|
103
|
+
}
|
|
104
|
+
if (section.renamed) {
|
|
105
|
+
return 'renamed';
|
|
106
|
+
}
|
|
107
|
+
return section.copied ? 'copied' : 'modified';
|
|
108
|
+
}
|
|
109
|
+
function linesOf(text, limits) {
|
|
110
|
+
const lines = [];
|
|
111
|
+
let start = 0;
|
|
112
|
+
while (lines.length < limits.maxLines && start <= limits.maxCharacters) {
|
|
113
|
+
const end = text.indexOf('\n', start);
|
|
114
|
+
if (end === -1) {
|
|
115
|
+
lines.push(text.slice(start).replace(/\r$/, ''));
|
|
116
|
+
return { lines, truncated: false };
|
|
117
|
+
}
|
|
118
|
+
lines.push(text.slice(start, end).replace(/\r$/, ''));
|
|
119
|
+
start = end + 1;
|
|
120
|
+
}
|
|
121
|
+
// Stopping right after a final newline leaves nothing unread.
|
|
122
|
+
return { lines, truncated: start < text.length };
|
|
123
|
+
}
|
|
124
|
+
/*
|
|
125
|
+
* What every line of a patch is, with the file numbers of its lines and the files it touches. It reads
|
|
126
|
+
* git patches and plain `diff -u` output, tolerates a text that was edited so its hunks no longer add up
|
|
127
|
+
* (the hunk is marked inconsistent and the reading goes on) and never throws. Past the limits it stops
|
|
128
|
+
* and says so in `truncated`.
|
|
129
|
+
*/
|
|
130
|
+
export function patchLayoutOf(text, limits = {}) {
|
|
131
|
+
const { lines: source, truncated } = linesOf(text, { ...DEFAULT_PATCH_LIMITS, ...limits });
|
|
132
|
+
const lines = [];
|
|
133
|
+
const sections = [];
|
|
134
|
+
let section;
|
|
135
|
+
let hunk;
|
|
136
|
+
let binaryData = false;
|
|
137
|
+
const seal = () => {
|
|
138
|
+
if (hunk !== undefined && (hunk.oldLeft > 0 || hunk.newLeft > 0)) {
|
|
139
|
+
hunk.layout.consistent = false;
|
|
140
|
+
}
|
|
141
|
+
hunk = undefined;
|
|
142
|
+
};
|
|
143
|
+
const open = (line, git) => {
|
|
144
|
+
seal();
|
|
145
|
+
binaryData = false;
|
|
146
|
+
section = emptySection(line, git);
|
|
147
|
+
sections.push(section);
|
|
148
|
+
return section;
|
|
149
|
+
};
|
|
150
|
+
// A removed line can read `-- x`, so `---` starts a file only with `+++` under it.
|
|
151
|
+
const startsFile = (index) => source[index].startsWith('--- ') && (source[index + 1]?.startsWith('+++ ') ?? false);
|
|
152
|
+
const push = (line, index) => {
|
|
153
|
+
lines.push(line);
|
|
154
|
+
if (section !== undefined) {
|
|
155
|
+
section.lastLine = index;
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
for (let index = 0; index < source.length; index++) {
|
|
159
|
+
const line = source[index];
|
|
160
|
+
const remaining = hunk !== undefined && (hunk.oldLeft > 0 || hunk.newLeft > 0);
|
|
161
|
+
const gitStart = line.startsWith('diff --git ');
|
|
162
|
+
const header = line.startsWith('@@') ? parseHunkHeader(line) : undefined;
|
|
163
|
+
const fileStart = startsFile(index) && (!remaining || source[index + 2]?.startsWith('@@') === true);
|
|
164
|
+
if (remaining && !gitStart && header === undefined && !fileStart) {
|
|
165
|
+
if (isContent(line) || line === '' || line.startsWith('\\')) {
|
|
166
|
+
push(contentLine(line, hunk, section), index);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
hunk.layout.consistent = false;
|
|
170
|
+
push(OTHER, index);
|
|
171
|
+
}
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (gitStart) {
|
|
175
|
+
const created = open(index, true);
|
|
176
|
+
({ oldPath: created.oldPath, newPath: created.newPath, prefixed: created.prefixed } = gitPaths(line.slice('diff --git '.length)));
|
|
177
|
+
push(FILE_HEADER, index);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if (fileStart) {
|
|
181
|
+
const reuse = section !== undefined && section.git && !section.named && section.hunks.length === 0;
|
|
182
|
+
const target = reuse ? section : open(index, false);
|
|
183
|
+
seal();
|
|
184
|
+
nameSides(target, line, source[index + 1]);
|
|
185
|
+
push(FILE_HEADER, index);
|
|
186
|
+
push(FILE_HEADER, index + 1);
|
|
187
|
+
index++;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (header !== undefined) {
|
|
191
|
+
seal();
|
|
192
|
+
const target = section ?? open(index, false);
|
|
193
|
+
const layout = { line: index, ...header, consistent: true };
|
|
194
|
+
target.hunks.push(layout);
|
|
195
|
+
hunk = { layout, oldLeft: header.oldCount, newLeft: header.newCount, oldNumber: header.oldStart, newNumber: header.newStart };
|
|
196
|
+
push(HUNK_HEADER, index);
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (section === undefined) {
|
|
200
|
+
push(OTHER, index);
|
|
201
|
+
}
|
|
202
|
+
else if (hunk !== undefined && line.startsWith('\\')) {
|
|
203
|
+
push(NO_NEWLINE, index);
|
|
204
|
+
}
|
|
205
|
+
else if (hunk !== undefined && isContent(line) && line !== '-- ') {
|
|
206
|
+
// The counts ran out, so a line a person typed below the hunk extends it. The `-- ` of a mail signature does not.
|
|
207
|
+
hunk.layout.consistent = false;
|
|
208
|
+
push(contentLine(line, hunk, section), index);
|
|
209
|
+
}
|
|
210
|
+
else if (hunk === undefined && section.git && GIT_HEADERS.test(line)) {
|
|
211
|
+
readGitHeader(section, line);
|
|
212
|
+
push(FILE_HEADER, index);
|
|
213
|
+
}
|
|
214
|
+
else if (/^Binary files .* differ$/.test(line) || line === 'GIT binary patch') {
|
|
215
|
+
section.binary = true;
|
|
216
|
+
binaryData = line === 'GIT binary patch';
|
|
217
|
+
push(BINARY, index);
|
|
218
|
+
}
|
|
219
|
+
else if (binaryData && line !== '') {
|
|
220
|
+
push(BINARY, index);
|
|
221
|
+
}
|
|
222
|
+
else if (line === '') {
|
|
223
|
+
lines.push(OTHER);
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
push(OTHER, index);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
seal();
|
|
230
|
+
return {
|
|
231
|
+
lines,
|
|
232
|
+
truncated,
|
|
233
|
+
files: sections.map((entry) => ({
|
|
234
|
+
oldPath: entry.oldPath,
|
|
235
|
+
newPath: entry.newPath,
|
|
236
|
+
status: statusOf(entry),
|
|
237
|
+
firstLine: entry.firstLine,
|
|
238
|
+
lastLine: entry.lastLine,
|
|
239
|
+
added: entry.added,
|
|
240
|
+
removed: entry.removed,
|
|
241
|
+
hunks: entry.hunks
|
|
242
|
+
}))
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
function isContent(line) {
|
|
246
|
+
return line.startsWith('+') || line.startsWith('-') || line.startsWith(' ');
|
|
247
|
+
}
|
|
248
|
+
/* A line under a hunk header; an empty line is a context line whose space an editor trimmed. */
|
|
249
|
+
function contentLine(line, hunk, section) {
|
|
250
|
+
if (line.startsWith('\\')) {
|
|
251
|
+
return NO_NEWLINE;
|
|
252
|
+
}
|
|
253
|
+
const kind = line.startsWith('+') ? 'added' : line.startsWith('-') ? 'removed' : 'context';
|
|
254
|
+
const takesOld = kind !== 'added';
|
|
255
|
+
const takesNew = kind !== 'removed';
|
|
256
|
+
if ((takesOld && hunk.oldLeft === 0) || (takesNew && hunk.newLeft === 0)) {
|
|
257
|
+
hunk.layout.consistent = false;
|
|
258
|
+
}
|
|
259
|
+
if (kind === 'added') {
|
|
260
|
+
section.added++;
|
|
261
|
+
}
|
|
262
|
+
else if (kind === 'removed') {
|
|
263
|
+
section.removed++;
|
|
264
|
+
}
|
|
265
|
+
const oldNumber = takesOld ? hunk.oldNumber++ : null;
|
|
266
|
+
const newNumber = takesNew ? hunk.newNumber++ : null;
|
|
267
|
+
hunk.oldLeft = takesOld ? Math.max(0, hunk.oldLeft - 1) : hunk.oldLeft;
|
|
268
|
+
hunk.newLeft = takesNew ? Math.max(0, hunk.newLeft - 1) : hunk.newLeft;
|
|
269
|
+
return { role: kind, old: oldNumber, new: newNumber };
|
|
270
|
+
}
|
|
271
|
+
function nameSides(section, oldLine, newLine) {
|
|
272
|
+
const oldName = pathOf(oldLine.slice(4));
|
|
273
|
+
const newName = pathOf(newLine.slice(4));
|
|
274
|
+
const prefixed = section.git ? section.prefixed : (oldName?.startsWith('a/') ?? false) && (newName?.startsWith('b/') ?? false);
|
|
275
|
+
section.oldPath = prefixed ? stripPrefix(oldName, 'a/') : oldName;
|
|
276
|
+
section.newPath = prefixed ? stripPrefix(newName, 'b/') : newName;
|
|
277
|
+
section.created ||= oldName === null;
|
|
278
|
+
section.deleted ||= newName === null;
|
|
279
|
+
section.named = true;
|
|
280
|
+
}
|
|
281
|
+
function readGitHeader(section, line) {
|
|
282
|
+
if (line.startsWith('new file mode ')) {
|
|
283
|
+
section.created = true;
|
|
284
|
+
}
|
|
285
|
+
else if (line.startsWith('deleted file mode ')) {
|
|
286
|
+
section.deleted = true;
|
|
287
|
+
}
|
|
288
|
+
else if (line.startsWith('rename from ')) {
|
|
289
|
+
section.renamed = true;
|
|
290
|
+
section.oldPath = pathOf(line.slice('rename from '.length));
|
|
291
|
+
}
|
|
292
|
+
else if (line.startsWith('rename to ')) {
|
|
293
|
+
section.renamed = true;
|
|
294
|
+
section.newPath = pathOf(line.slice('rename to '.length));
|
|
295
|
+
}
|
|
296
|
+
else if (line.startsWith('copy from ')) {
|
|
297
|
+
section.copied = true;
|
|
298
|
+
section.oldPath = pathOf(line.slice('copy from '.length));
|
|
299
|
+
}
|
|
300
|
+
else if (line.startsWith('copy to ')) {
|
|
301
|
+
section.copied = true;
|
|
302
|
+
section.newPath = pathOf(line.slice('copy to '.length));
|
|
303
|
+
}
|
|
304
|
+
}
|
package/dist/peek.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { EditorPosition } from '@adecore/editor';
|
|
2
2
|
import type { EditorLanguage } from './editor-language.ts';
|
|
3
3
|
import type { PeekView } from './popups.ts';
|
|
4
|
+
import { type Timers } from './timers.ts';
|
|
4
5
|
export declare const PEEK_HEIGHT = 241;
|
|
5
6
|
type PeekKind = PeekView['kind'];
|
|
6
7
|
export declare class PeekFeature {
|
|
7
8
|
private readonly language;
|
|
9
|
+
private readonly timers;
|
|
8
10
|
private kind;
|
|
9
11
|
private targets;
|
|
10
12
|
private texts;
|
|
@@ -12,7 +14,7 @@ export declare class PeekFeature {
|
|
|
12
14
|
private order;
|
|
13
15
|
private line;
|
|
14
16
|
private token;
|
|
15
|
-
constructor(language: EditorLanguage);
|
|
17
|
+
constructor(language: EditorLanguage, timers?: Timers);
|
|
16
18
|
get isOpen(): boolean;
|
|
17
19
|
open(position?: EditorPosition, kind?: PeekKind): Promise<void>;
|
|
18
20
|
openDefinition(position?: EditorPosition): Promise<void>;
|
package/dist/peek.js
CHANGED
|
@@ -3,6 +3,7 @@ import { uniquePlaces } from './navigation.js';
|
|
|
3
3
|
import { PEEK_READ_FILES, definitionSnippetOf, peekFilesOf, snippetOf } from './peek-model.js';
|
|
4
4
|
import { isShortcut } from './shortcut-keys.js';
|
|
5
5
|
import { messageOf } from './error-message.js';
|
|
6
|
+
import { realTimers, revealRow } from './timers.js';
|
|
6
7
|
const TOAST_ID = 'language-peek';
|
|
7
8
|
/* The block as the panel draws it: a hatched band, the header, eight lines of code and a hatched band, until the editor has measured it. */
|
|
8
9
|
export const PEEK_HEIGHT = 241;
|
|
@@ -20,6 +21,7 @@ function placeKey(location) {
|
|
|
20
21
|
*/
|
|
21
22
|
export class PeekFeature {
|
|
22
23
|
language;
|
|
24
|
+
timers;
|
|
23
25
|
kind = 'references';
|
|
24
26
|
/* The whole declaration of each place of a definition peek, where the server gave it. */
|
|
25
27
|
targets = new Map();
|
|
@@ -28,8 +30,9 @@ export class PeekFeature {
|
|
|
28
30
|
order = [];
|
|
29
31
|
line = 0;
|
|
30
32
|
token = 0;
|
|
31
|
-
constructor(language) {
|
|
33
|
+
constructor(language, timers = realTimers) {
|
|
32
34
|
this.language = language;
|
|
35
|
+
this.timers = timers;
|
|
33
36
|
const { editor } = language;
|
|
34
37
|
const offs = [
|
|
35
38
|
editor.onKeyDown((event) => {
|
|
@@ -112,6 +115,7 @@ export class PeekFeature {
|
|
|
112
115
|
? files[0].places[0]
|
|
113
116
|
: (files.flatMap((file) => file.places).find((place) => !(place.location.uri === uri && place.line === position.line)) ?? files[0].places[0]);
|
|
114
117
|
this.line = position.line;
|
|
118
|
+
this.language.callHierarchy.close();
|
|
115
119
|
this.language.popups.setState({ peek: { kind, container: null, files, active: first.id, collapsed: [], count: unique.length, preview: null } });
|
|
116
120
|
this.select(first.id);
|
|
117
121
|
// The editor draws a row once per id, so a peek opened over another needs an id of its own to get a container.
|
|
@@ -178,6 +182,7 @@ export class PeekFeature {
|
|
|
178
182
|
const view = this.language.popups.getState().peek;
|
|
179
183
|
if (view !== null && view.container !== container) {
|
|
180
184
|
this.language.popups.setState({ peek: { ...view, container } });
|
|
185
|
+
revealRow(this.timers, container);
|
|
181
186
|
}
|
|
182
187
|
}
|
|
183
188
|
key(event) {
|
package/dist/pick.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export interface PickSpec {
|
|
|
8
8
|
readonly groups: readonly PickGroup[];
|
|
9
9
|
readonly active?: string;
|
|
10
10
|
accept(id: string): void;
|
|
11
|
+
onTab?(backwards: boolean): void;
|
|
11
12
|
attempt?(id: string): Promise<string | null>;
|
|
12
13
|
preview?(id: string, signal: AbortSignal): Promise<PickPreview | null>;
|
|
13
14
|
}
|
package/dist/pick.js
CHANGED
|
@@ -125,9 +125,19 @@ export class PickFeature {
|
|
|
125
125
|
this.activate(this.ids[this.ids.length - 1]);
|
|
126
126
|
return true;
|
|
127
127
|
case 'Enter':
|
|
128
|
-
case 'Tab':
|
|
129
128
|
this.choose(this.active);
|
|
130
129
|
return true;
|
|
130
|
+
case 'Tab': {
|
|
131
|
+
const spec = this.spec;
|
|
132
|
+
if (spec.onTab !== undefined) {
|
|
133
|
+
this.close();
|
|
134
|
+
spec.onTab(event.shiftKey);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
this.choose(this.active);
|
|
138
|
+
}
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
131
141
|
case 'Escape':
|
|
132
142
|
this.close();
|
|
133
143
|
return true;
|
package/dist/popups.d.ts
CHANGED
|
@@ -4,10 +4,12 @@ import type { Location } from '@adecore/lsp';
|
|
|
4
4
|
import type { CodeAuthorship } from './code-authors.ts';
|
|
5
5
|
import type { Problem } from './diagnostics-model.ts';
|
|
6
6
|
import type { PeekFile, PeekSnippet } from './peek-model.ts';
|
|
7
|
+
import type { CallDirection, CallRow } from './call-hierarchy-model.ts';
|
|
7
8
|
import type { ActionEntry } from './code-actions-model.ts';
|
|
8
9
|
import type { SymbolEntry } from './symbol-picker-model.ts';
|
|
9
10
|
import type { HoverText } from './hover-content.ts';
|
|
10
11
|
import type { SignatureViewModel } from './signature-model.ts';
|
|
12
|
+
import type { ResourceOperationView } from './workspace-edit.ts';
|
|
11
13
|
export interface HoverView {
|
|
12
14
|
readonly anchor: EditorPosition;
|
|
13
15
|
readonly position: EditorPosition;
|
|
@@ -20,6 +22,7 @@ export interface HoverInfo {
|
|
|
20
22
|
readonly definition: Location | null;
|
|
21
23
|
readonly word: string;
|
|
22
24
|
readonly references: number | null;
|
|
25
|
+
readonly plain?: boolean;
|
|
23
26
|
}
|
|
24
27
|
export interface CompletionRow {
|
|
25
28
|
readonly label: string;
|
|
@@ -90,6 +93,7 @@ export interface RenameView {
|
|
|
90
93
|
readonly error: string | null;
|
|
91
94
|
readonly name: string;
|
|
92
95
|
readonly files: readonly RenameFileView[];
|
|
96
|
+
readonly operations?: readonly ResourceOperationView[];
|
|
93
97
|
readonly suggestions: readonly string[];
|
|
94
98
|
}
|
|
95
99
|
export interface PeekView {
|
|
@@ -103,6 +107,18 @@ export interface PeekView {
|
|
|
103
107
|
readonly uri: string;
|
|
104
108
|
}) | null;
|
|
105
109
|
}
|
|
110
|
+
export interface CallHierarchyView {
|
|
111
|
+
readonly direction: CallDirection;
|
|
112
|
+
readonly container: HTMLElement | null;
|
|
113
|
+
readonly name: string;
|
|
114
|
+
readonly rows: readonly CallRow[];
|
|
115
|
+
readonly active: string;
|
|
116
|
+
readonly site: number;
|
|
117
|
+
readonly sites: number;
|
|
118
|
+
readonly preview: (PeekSnippet & {
|
|
119
|
+
readonly uri: string;
|
|
120
|
+
}) | null;
|
|
121
|
+
}
|
|
106
122
|
export interface SymbolsView {
|
|
107
123
|
readonly file: string;
|
|
108
124
|
readonly entries: readonly SymbolEntry[];
|
|
@@ -125,6 +141,7 @@ export interface PopupState {
|
|
|
125
141
|
readonly pick: PickView | null;
|
|
126
142
|
readonly rename: RenameView | null;
|
|
127
143
|
readonly peek: PeekView | null;
|
|
144
|
+
readonly callHierarchy?: CallHierarchyView | null;
|
|
128
145
|
readonly symbols: SymbolsView | null;
|
|
129
146
|
readonly menu: MenuView | null;
|
|
130
147
|
readonly authors: AuthorsView | null;
|
package/dist/popups.js
CHANGED
|
@@ -10,6 +10,10 @@ export interface LanguageDocumentHandle {
|
|
|
10
10
|
readonly ready: Promise<void>;
|
|
11
11
|
release(): void;
|
|
12
12
|
}
|
|
13
|
+
export type FileOperationTarget = string | {
|
|
14
|
+
readonly path: string;
|
|
15
|
+
readonly directory?: boolean;
|
|
16
|
+
};
|
|
13
17
|
export declare class ProjectLanguage {
|
|
14
18
|
readonly service: LanguageService;
|
|
15
19
|
readonly host: LanguageHost;
|
|
@@ -19,12 +23,20 @@ export declare class ProjectLanguage {
|
|
|
19
23
|
readonly history: NavigationHistory;
|
|
20
24
|
private readonly holders;
|
|
21
25
|
private readonly files;
|
|
26
|
+
panelBodyHeight: number;
|
|
27
|
+
private readonly panelListeners;
|
|
22
28
|
private pendingCaret;
|
|
23
29
|
private lastPlace;
|
|
24
30
|
private disposed;
|
|
25
31
|
constructor(service: LanguageService, host?: LanguageHost);
|
|
32
|
+
onPanelBodyChange(listener: (height: number) => void): () => void;
|
|
33
|
+
resizePanelBody(height: number): void;
|
|
26
34
|
pathOfLocation(uri: string): string | null;
|
|
27
35
|
applyWorkspaceEdit(edit: WorkspaceEdit): Promise<ApplyWorkspaceEditResult>;
|
|
36
|
+
willCreateFiles(targets: readonly FileOperationTarget[]): Promise<WorkspaceEdit | null>;
|
|
37
|
+
didCreateFiles(targets: readonly FileOperationTarget[]): Promise<void>;
|
|
38
|
+
willDeleteFiles(targets: readonly FileOperationTarget[]): Promise<WorkspaceEdit | null>;
|
|
39
|
+
didDeleteFiles(targets: readonly FileOperationTarget[]): Promise<void>;
|
|
28
40
|
readText(uri: string): Promise<string | null>;
|
|
29
41
|
openPlace(place: Place): void;
|
|
30
42
|
noteCaret(place: Place): void;
|
package/dist/project-language.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { disposeSnippetTransforms } from './snippet-transform-queue.js';
|
|
1
2
|
import i18next, {} from 'i18next';
|
|
2
|
-
import { fileUriToPath } from '@adecore/lsp';
|
|
3
|
+
import { fileUriToPath, pathToFileUri } from '@adecore/lsp';
|
|
3
4
|
import { NavigationHistory } from './navigation-history.js';
|
|
4
5
|
import { ProjectProblems } from './project-problems.js';
|
|
5
6
|
import { applyWorkspaceEdit } from './workspace-edit.js';
|
|
6
7
|
import { relativeTo } from './paths.js';
|
|
8
|
+
import { PANEL_BODY_HEIGHT } from './panel-size.js';
|
|
7
9
|
/* Sends one editor's edits to the document, holding them until the document is open. */
|
|
8
10
|
class DocumentSync {
|
|
9
11
|
ready;
|
|
@@ -51,6 +53,12 @@ class DocumentSync {
|
|
|
51
53
|
void this.service.changeDocument(this.uri, changes).catch(() => undefined);
|
|
52
54
|
}
|
|
53
55
|
}
|
|
56
|
+
function filesOf(targets) {
|
|
57
|
+
return targets.map((target) => {
|
|
58
|
+
const { path, directory } = typeof target === 'string' ? { path: target, directory: false } : { path: target.path, directory: target.directory === true };
|
|
59
|
+
return { uri: pathToFileUri(path), directory };
|
|
60
|
+
});
|
|
61
|
+
}
|
|
54
62
|
/*
|
|
55
63
|
* A shared language service and document ownership for the editors of a project. Two editors on the same
|
|
56
64
|
* file hold one document, and only the first one sends changes, since both would report the same edit.
|
|
@@ -64,6 +72,9 @@ export class ProjectLanguage {
|
|
|
64
72
|
history = new NavigationHistory();
|
|
65
73
|
holders = new Map();
|
|
66
74
|
files;
|
|
75
|
+
/* The body height of the peek and call hierarchy panels, shared by the editors of the project. */
|
|
76
|
+
panelBodyHeight;
|
|
77
|
+
panelListeners = new Set();
|
|
67
78
|
pendingCaret = null;
|
|
68
79
|
lastPlace = null;
|
|
69
80
|
disposed = false;
|
|
@@ -73,8 +84,22 @@ export class ProjectLanguage {
|
|
|
73
84
|
this.i18n = host.i18n ?? i18next;
|
|
74
85
|
this.folder = host.folder ?? '';
|
|
75
86
|
this.files = host.files ?? null;
|
|
87
|
+
this.panelBodyHeight = host.panelBodyHeight ?? PANEL_BODY_HEIGHT;
|
|
76
88
|
this.problems = new ProjectProblems(service, (uri) => this.pathOfLocation(uri) ?? uri);
|
|
77
89
|
}
|
|
90
|
+
/* Open panels of other editors of the project follow a resize. */
|
|
91
|
+
onPanelBodyChange(listener) {
|
|
92
|
+
this.panelListeners.add(listener);
|
|
93
|
+
return () => this.panelListeners.delete(listener);
|
|
94
|
+
}
|
|
95
|
+
resizePanelBody(height) {
|
|
96
|
+
if (height === this.panelBodyHeight) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
this.panelBodyHeight = height;
|
|
100
|
+
this.panelListeners.forEach((listener) => listener(height));
|
|
101
|
+
this.host.onPanelResize?.(height);
|
|
102
|
+
}
|
|
78
103
|
pathOfLocation(uri) {
|
|
79
104
|
if (this.host.pathOfUri !== undefined) {
|
|
80
105
|
return this.host.pathOfUri(uri);
|
|
@@ -84,7 +109,31 @@ export class ProjectLanguage {
|
|
|
84
109
|
}
|
|
85
110
|
/* Makes an edit of a language server: an open document takes it as one undo step and any other file gets an unsaved draft. */
|
|
86
111
|
applyWorkspaceEdit(edit) {
|
|
87
|
-
|
|
112
|
+
const { service } = this;
|
|
113
|
+
return applyWorkspaceEdit(edit, {
|
|
114
|
+
editorOf: (uri) => this.holders.get(uri)?.editors[0],
|
|
115
|
+
files: this.files,
|
|
116
|
+
...(service.versionOf === undefined ? {} : { versionOf: (uri) => service.versionOf(uri) })
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/*
|
|
120
|
+
* What the servers want changed before files are made, for a host that makes them outside a language server's
|
|
121
|
+
* edit: apply the edit with `applyWorkspaceEdit`, then make the files. Null when no server has anything to say.
|
|
122
|
+
*/
|
|
123
|
+
willCreateFiles(targets) {
|
|
124
|
+
return this.service.willCreateFiles?.(filesOf(targets)) ?? Promise.resolve(null);
|
|
125
|
+
}
|
|
126
|
+
/* Tells the servers that files were made, once they are. */
|
|
127
|
+
didCreateFiles(targets) {
|
|
128
|
+
return this.service.didCreateFiles?.(filesOf(targets)) ?? Promise.resolve();
|
|
129
|
+
}
|
|
130
|
+
/* What the servers want changed before files are deleted, such as the references to them; null when no server has anything to say. */
|
|
131
|
+
willDeleteFiles(targets) {
|
|
132
|
+
return this.service.willDeleteFiles?.(filesOf(targets)) ?? Promise.resolve(null);
|
|
133
|
+
}
|
|
134
|
+
/* Tells the servers that files were deleted, once they are. */
|
|
135
|
+
didDeleteFiles(targets) {
|
|
136
|
+
return this.service.didDeleteFiles?.(filesOf(targets)) ?? Promise.resolve();
|
|
88
137
|
}
|
|
89
138
|
/* The text of a file as it stands now: an open document's, else a draft's or the machine's; null when it is no text file. */
|
|
90
139
|
async readText(uri) {
|
|
@@ -179,6 +228,7 @@ export class ProjectLanguage {
|
|
|
179
228
|
return;
|
|
180
229
|
}
|
|
181
230
|
this.disposed = true;
|
|
231
|
+
disposeSnippetTransforms(this);
|
|
182
232
|
for (const [uri, holder] of this.holders) {
|
|
183
233
|
holder.sync.dispose();
|
|
184
234
|
void holder.sync.ready
|
package/dist/refresher.d.ts
CHANGED
package/dist/refresher.js
CHANGED
|
@@ -36,6 +36,11 @@ export class Refresher {
|
|
|
36
36
|
}
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
|
+
/* Drops the wait and the ask in flight, and keeps the refresher usable. */
|
|
40
|
+
cancel() {
|
|
41
|
+
this.timers.clear(this.timer);
|
|
42
|
+
this.controller?.abort();
|
|
43
|
+
}
|
|
39
44
|
dispose() {
|
|
40
45
|
this.disposed = true;
|
|
41
46
|
this.timers.clear(this.timer);
|
package/dist/rename-model.d.ts
CHANGED
|
@@ -22,4 +22,4 @@ export interface RenameFile {
|
|
|
22
22
|
readonly rows: readonly RenameRow[];
|
|
23
23
|
}
|
|
24
24
|
export declare function renameRowsOf(text: string, edits: readonly TextEdit[]): RenameRow[];
|
|
25
|
-
export declare function renamePreviewOf(edit: WorkspaceEdit, textOf: (uri: string) => Promise<string | null>): Promise<RenameFile[]
|
|
25
|
+
export declare function renamePreviewOf(edit: WorkspaceEdit, textOf: (uri: string) => Promise<string | null>): Promise<RenameFile[]>;
|
package/dist/rename-model.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isIdentifierCharacter } from './completion-model.js';
|
|
2
|
-
import {
|
|
2
|
+
import { textEntriesOf } from './workspace-edit.js';
|
|
3
3
|
/* The identifier the caret is in or touches; null where there is none. */
|
|
4
4
|
export function wordRangeAt(lineText, position) {
|
|
5
5
|
let start = Math.min(position.character, lineText.length);
|
|
@@ -78,14 +78,10 @@ export function renameRowsOf(text, edits) {
|
|
|
78
78
|
return { line: line + 1, before, after };
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
|
-
/*
|
|
81
|
+
/* The lines of the files an edit edits; the files it makes, moves or deletes are listed by `resourceOperationsOf`. */
|
|
82
82
|
export async function renamePreviewOf(edit, textOf) {
|
|
83
|
-
const entries = entriesOf(edit);
|
|
84
|
-
if (entries === null) {
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
83
|
const files = [];
|
|
88
|
-
for (const [uri, list] of
|
|
84
|
+
for (const [uri, list] of textEntriesOf(edit)) {
|
|
89
85
|
const text = await textOf(uri);
|
|
90
86
|
files.push({ uri, rows: text === null ? [] : renameRowsOf(text, list.flat()) });
|
|
91
87
|
}
|