@adecore/editor-react 0.22.1 → 0.23.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/CallHierarchyPanel.d.ts +6 -0
- package/dist/CallHierarchyPanel.js +88 -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/PeekPanel.d.ts +3 -0
- package/dist/PeekPanel.js +2 -3
- 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 +99 -0
- package/dist/call-hierarchy.d.ts +42 -0
- package/dist/call-hierarchy.js +399 -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 +14 -4
- 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 +17 -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 +114 -3
- package/dist/locales/nl.json +114 -3
- package/dist/models.d.ts +6 -2
- package/dist/models.js +5 -1
- 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.js +1 -0
- 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 +8 -0
- package/dist/project-language.js +34 -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 +62 -0
- package/dist/rendered-docs.js +368 -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/workspace-edit.d.ts +25 -3
- package/dist/workspace-edit.js +282 -143
- package/package.json +10 -5
- package/src/CallHierarchyPanel.tsx +238 -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/PeekPanel.tsx +2 -3
- 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 +158 -0
- package/src/call-hierarchy.ts +441 -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 +21 -5
- package/src/editor-react.css +79 -0
- package/src/format-on-type.ts +111 -0
- package/src/host-types.ts +19 -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 +114 -3
- package/src/locales/nl.json +114 -3
- package/src/models.ts +40 -1
- package/src/patch-decorations.ts +224 -0
- package/src/patch-layout.ts +388 -0
- package/src/peek.ts +1 -0
- package/src/pick.ts +12 -1
- package/src/popups.ts +24 -0
- package/src/project-language.ts +43 -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 +422 -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/workspace-edit.ts +347 -169
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type PatchLineRole = 'file-header' | 'hunk-header' | 'context' | 'added' | 'removed' | 'no-newline' | 'binary' | 'other';
|
|
2
|
+
export type PatchFileStatus = 'added' | 'deleted' | 'renamed' | 'copied' | 'modified' | 'binary';
|
|
3
|
+
export interface PatchLayoutLine {
|
|
4
|
+
readonly role: PatchLineRole;
|
|
5
|
+
readonly old: number | null;
|
|
6
|
+
readonly new: number | null;
|
|
7
|
+
}
|
|
8
|
+
export interface PatchHunkLayout {
|
|
9
|
+
readonly line: number;
|
|
10
|
+
readonly oldStart: number;
|
|
11
|
+
readonly oldCount: number;
|
|
12
|
+
readonly newStart: number;
|
|
13
|
+
readonly newCount: number;
|
|
14
|
+
readonly section: string;
|
|
15
|
+
readonly consistent: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface PatchFileLayout {
|
|
18
|
+
readonly oldPath: string | null;
|
|
19
|
+
readonly newPath: string | null;
|
|
20
|
+
readonly status: PatchFileStatus;
|
|
21
|
+
readonly firstLine: number;
|
|
22
|
+
readonly lastLine: number;
|
|
23
|
+
readonly added: number;
|
|
24
|
+
readonly removed: number;
|
|
25
|
+
readonly hunks: readonly PatchHunkLayout[];
|
|
26
|
+
}
|
|
27
|
+
export interface PatchLayout {
|
|
28
|
+
readonly lines: readonly PatchLayoutLine[];
|
|
29
|
+
readonly files: readonly PatchFileLayout[];
|
|
30
|
+
readonly truncated: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface PatchLimits {
|
|
33
|
+
readonly maxLines?: number;
|
|
34
|
+
readonly maxCharacters?: number;
|
|
35
|
+
}
|
|
36
|
+
export declare const DEFAULT_PATCH_LIMITS: {
|
|
37
|
+
readonly maxLines: 50000;
|
|
38
|
+
readonly maxCharacters: 4000000;
|
|
39
|
+
};
|
|
40
|
+
export declare function patchLayoutOf(text: string, limits?: PatchLimits): PatchLayout;
|
|
@@ -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.js
CHANGED
|
@@ -112,6 +112,7 @@ export class PeekFeature {
|
|
|
112
112
|
? files[0].places[0]
|
|
113
113
|
: (files.flatMap((file) => file.places).find((place) => !(place.location.uri === uri && place.line === position.line)) ?? files[0].places[0]);
|
|
114
114
|
this.line = position.line;
|
|
115
|
+
this.language.callHierarchy.close();
|
|
115
116
|
this.language.popups.setState({ peek: { kind, container: null, files, active: first.id, collapsed: [], count: unique.length, preview: null } });
|
|
116
117
|
this.select(first.id);
|
|
117
118
|
// The editor draws a row once per id, so a peek opened over another needs an id of its own to get a container.
|
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;
|
|
@@ -25,6 +29,10 @@ export declare class ProjectLanguage {
|
|
|
25
29
|
constructor(service: LanguageService, host?: LanguageHost);
|
|
26
30
|
pathOfLocation(uri: string): string | null;
|
|
27
31
|
applyWorkspaceEdit(edit: WorkspaceEdit): Promise<ApplyWorkspaceEditResult>;
|
|
32
|
+
willCreateFiles(targets: readonly FileOperationTarget[]): Promise<WorkspaceEdit | null>;
|
|
33
|
+
didCreateFiles(targets: readonly FileOperationTarget[]): Promise<void>;
|
|
34
|
+
willDeleteFiles(targets: readonly FileOperationTarget[]): Promise<WorkspaceEdit | null>;
|
|
35
|
+
didDeleteFiles(targets: readonly FileOperationTarget[]): Promise<void>;
|
|
28
36
|
readText(uri: string): Promise<string | null>;
|
|
29
37
|
openPlace(place: Place): void;
|
|
30
38
|
noteCaret(place: Place): void;
|
package/dist/project-language.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
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';
|
|
@@ -51,6 +52,12 @@ class DocumentSync {
|
|
|
51
52
|
void this.service.changeDocument(this.uri, changes).catch(() => undefined);
|
|
52
53
|
}
|
|
53
54
|
}
|
|
55
|
+
function filesOf(targets) {
|
|
56
|
+
return targets.map((target) => {
|
|
57
|
+
const { path, directory } = typeof target === 'string' ? { path: target, directory: false } : { path: target.path, directory: target.directory === true };
|
|
58
|
+
return { uri: pathToFileUri(path), directory };
|
|
59
|
+
});
|
|
60
|
+
}
|
|
54
61
|
/*
|
|
55
62
|
* A shared language service and document ownership for the editors of a project. Two editors on the same
|
|
56
63
|
* file hold one document, and only the first one sends changes, since both would report the same edit.
|
|
@@ -84,7 +91,31 @@ export class ProjectLanguage {
|
|
|
84
91
|
}
|
|
85
92
|
/* 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
93
|
applyWorkspaceEdit(edit) {
|
|
87
|
-
|
|
94
|
+
const { service } = this;
|
|
95
|
+
return applyWorkspaceEdit(edit, {
|
|
96
|
+
editorOf: (uri) => this.holders.get(uri)?.editors[0],
|
|
97
|
+
files: this.files,
|
|
98
|
+
...(service.versionOf === undefined ? {} : { versionOf: (uri) => service.versionOf(uri) })
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/*
|
|
102
|
+
* What the servers want changed before files are made, for a host that makes them outside a language server's
|
|
103
|
+
* edit: apply the edit with `applyWorkspaceEdit`, then make the files. Null when no server has anything to say.
|
|
104
|
+
*/
|
|
105
|
+
willCreateFiles(targets) {
|
|
106
|
+
return this.service.willCreateFiles?.(filesOf(targets)) ?? Promise.resolve(null);
|
|
107
|
+
}
|
|
108
|
+
/* Tells the servers that files were made, once they are. */
|
|
109
|
+
didCreateFiles(targets) {
|
|
110
|
+
return this.service.didCreateFiles?.(filesOf(targets)) ?? Promise.resolve();
|
|
111
|
+
}
|
|
112
|
+
/* What the servers want changed before files are deleted, such as the references to them; null when no server has anything to say. */
|
|
113
|
+
willDeleteFiles(targets) {
|
|
114
|
+
return this.service.willDeleteFiles?.(filesOf(targets)) ?? Promise.resolve(null);
|
|
115
|
+
}
|
|
116
|
+
/* Tells the servers that files were deleted, once they are. */
|
|
117
|
+
didDeleteFiles(targets) {
|
|
118
|
+
return this.service.didDeleteFiles?.(filesOf(targets)) ?? Promise.resolve();
|
|
88
119
|
}
|
|
89
120
|
/* 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
121
|
async readText(uri) {
|
|
@@ -179,6 +210,7 @@ export class ProjectLanguage {
|
|
|
179
210
|
return;
|
|
180
211
|
}
|
|
181
212
|
this.disposed = true;
|
|
213
|
+
disposeSnippetTransforms(this);
|
|
182
214
|
for (const [uri, holder] of this.holders) {
|
|
183
215
|
holder.sync.dispose();
|
|
184
216
|
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
|
}
|
package/dist/rename.js
CHANGED
|
@@ -2,6 +2,7 @@ import { StaleResultError } from '@adecore/lsp';
|
|
|
2
2
|
import { lineTextOf } from './line-text.js';
|
|
3
3
|
import { occurrencesOf, renamePreviewOf, renameTargetOf, wordRangeAt } from './rename-model.js';
|
|
4
4
|
import { isShortcut } from './shortcut-keys.js';
|
|
5
|
+
import { resourceOperationsOf } from './workspace-edit.js';
|
|
5
6
|
import { messageOf } from './error-message.js';
|
|
6
7
|
const METHOD = 'textDocument/rename';
|
|
7
8
|
const TOAST_ID = 'language-rename';
|
|
@@ -66,7 +67,7 @@ export class RenameFeature {
|
|
|
66
67
|
}
|
|
67
68
|
const session = { target, original: editor.textInRange(target.range), edit: null, names: new AbortController() };
|
|
68
69
|
this.session = session;
|
|
69
|
-
this.publish({ phase: 'input', busy: false, name: target.placeholder, files: [], occurrences: null, suggestions: [] });
|
|
70
|
+
this.publish({ phase: 'input', busy: false, name: target.placeholder, files: [], operations: [], occurrences: null, suggestions: [] });
|
|
70
71
|
void this.light(session).then((locations) => this.suggest(session, locations));
|
|
71
72
|
}
|
|
72
73
|
/* The name was typed: Enter applies it, Shift+Enter lists what it would change first. */
|
|
@@ -100,12 +101,7 @@ export class RenameFeature {
|
|
|
100
101
|
if (this.session !== session) {
|
|
101
102
|
return;
|
|
102
103
|
}
|
|
103
|
-
|
|
104
|
-
// An edit that renames files too has no list of lines to show, so it is applied or refused as a whole.
|
|
105
|
-
await this.apply(session);
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
this.publish({ phase: 'preview', busy: false, name: next, files });
|
|
104
|
+
this.publish({ phase: 'preview', busy: false, name: next, files, operations: resourceOperationsOf(edit) });
|
|
109
105
|
}
|
|
110
106
|
catch (error) {
|
|
111
107
|
if (this.session === session) {
|
|
@@ -148,7 +144,7 @@ export class RenameFeature {
|
|
|
148
144
|
fail(message) {
|
|
149
145
|
if (this.session !== null) {
|
|
150
146
|
this.session.edit = null;
|
|
151
|
-
this.publish({ phase: 'input', busy: false, files: [], error: this.say('failed', { message }) });
|
|
147
|
+
this.publish({ phase: 'input', busy: false, files: [], operations: [], error: this.say('failed', { message }) });
|
|
152
148
|
}
|
|
153
149
|
}
|
|
154
150
|
finish() {
|
|
@@ -244,6 +240,7 @@ export class RenameFeature {
|
|
|
244
240
|
error: null,
|
|
245
241
|
name: session.target.placeholder,
|
|
246
242
|
files: [],
|
|
243
|
+
operations: [],
|
|
247
244
|
suggestions: [],
|
|
248
245
|
...current,
|
|
249
246
|
...patch
|