@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/patch-layout.js
CHANGED
|
@@ -1,125 +1,184 @@
|
|
|
1
1
|
import { parseHunkHeader } from '@adecore/merge';
|
|
2
|
-
export const DEFAULT_PATCH_LIMITS = {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
2
|
+
export const DEFAULT_PATCH_LIMITS = {
|
|
3
|
+
maxLines: 50_000,
|
|
4
|
+
maxCharacters: 4_000_000
|
|
5
|
+
};
|
|
6
|
+
const OTHER = {
|
|
7
|
+
role: 'other',
|
|
8
|
+
old: null,
|
|
9
|
+
new: null
|
|
10
|
+
};
|
|
11
|
+
const FILE_HEADER = {
|
|
12
|
+
role: 'file-header',
|
|
13
|
+
old: null,
|
|
14
|
+
new: null
|
|
15
|
+
};
|
|
16
|
+
const HUNK_HEADER = {
|
|
17
|
+
role: 'hunk-header',
|
|
18
|
+
old: null,
|
|
19
|
+
new: null
|
|
20
|
+
};
|
|
21
|
+
const NO_NEWLINE = {
|
|
22
|
+
role: 'no-newline',
|
|
23
|
+
old: null,
|
|
24
|
+
new: null
|
|
25
|
+
};
|
|
26
|
+
const BINARY = {
|
|
27
|
+
role: 'binary',
|
|
28
|
+
old: null,
|
|
29
|
+
new: null
|
|
30
|
+
};
|
|
8
31
|
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 = {
|
|
32
|
+
const ESCAPES = {
|
|
33
|
+
a: 7,
|
|
34
|
+
b: 8,
|
|
35
|
+
f: 12,
|
|
36
|
+
n: 10,
|
|
37
|
+
r: 13,
|
|
38
|
+
t: 9,
|
|
39
|
+
v: 11,
|
|
40
|
+
'"': 34,
|
|
41
|
+
'\\': 92
|
|
42
|
+
};
|
|
10
43
|
/* Reads a path as git writes it: bare, or in quotes with C escapes and octal bytes. */
|
|
11
44
|
function unquote(raw) {
|
|
12
|
-
|
|
13
|
-
|
|
45
|
+
if (raw.length < 2 || !raw.startsWith('"') || !raw.endsWith('"')) {
|
|
46
|
+
return raw;
|
|
47
|
+
}
|
|
48
|
+
const bytes = [];
|
|
49
|
+
const encoder = new TextEncoder();
|
|
50
|
+
const body = raw.slice(1, -1);
|
|
51
|
+
for (let i = 0; i < body.length; i++) {
|
|
52
|
+
if (body[i] !== '\\') {
|
|
53
|
+
const point = String.fromCodePoint(body.codePointAt(i));
|
|
54
|
+
bytes.push(...encoder.encode(point));
|
|
55
|
+
i += point.length - 1;
|
|
56
|
+
continue;
|
|
14
57
|
}
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
}
|
|
58
|
+
const octal = /^[0-3][0-7]{2}/.exec(body.slice(i + 1, i + 4));
|
|
59
|
+
const escaped = ESCAPES[body[i + 1] ?? ''];
|
|
60
|
+
if (octal !== null) {
|
|
61
|
+
bytes.push(Number.parseInt(octal[0], 8));
|
|
62
|
+
i += 3;
|
|
63
|
+
} else if (escaped !== undefined) {
|
|
64
|
+
bytes.push(escaped);
|
|
65
|
+
i++;
|
|
66
|
+
} else {
|
|
67
|
+
bytes.push(92);
|
|
38
68
|
}
|
|
39
|
-
|
|
69
|
+
}
|
|
70
|
+
return new TextDecoder().decode(new Uint8Array(bytes));
|
|
40
71
|
}
|
|
41
72
|
/* The name on a `---`, `+++`, `rename` or `copy` line: a timestamp after a tab goes, and `/dev/null` is no file. */
|
|
42
73
|
function pathOf(raw) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
74
|
+
const tab = raw.indexOf('\t');
|
|
75
|
+
const name = unquote(tab === -1 ? raw : raw.slice(0, tab));
|
|
76
|
+
return name === '/dev/null' ? null : name;
|
|
46
77
|
}
|
|
47
78
|
function stripPrefix(path, prefix) {
|
|
48
|
-
|
|
79
|
+
return path !== null && path.startsWith(prefix) ? path.slice(2) : path;
|
|
49
80
|
}
|
|
50
81
|
/* 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
82
|
function gitPaths(rest) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
83
|
+
if (rest.startsWith('"')) {
|
|
84
|
+
const end = rest.indexOf('" ', 1);
|
|
85
|
+
if (end !== -1) {
|
|
86
|
+
const oldName = unquote(rest.slice(0, end + 1));
|
|
87
|
+
const newName = unquote(rest.slice(end + 2));
|
|
88
|
+
const prefixed = oldName.startsWith('a/') && newName.startsWith('b/');
|
|
89
|
+
return prefixed ? {
|
|
90
|
+
oldPath: oldName.slice(2),
|
|
91
|
+
newPath: newName.slice(2),
|
|
92
|
+
prefixed
|
|
93
|
+
} : {
|
|
94
|
+
oldPath: oldName,
|
|
95
|
+
newPath: newName,
|
|
96
|
+
prefixed
|
|
97
|
+
};
|
|
60
98
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
99
|
+
}
|
|
100
|
+
const middle = rest.indexOf(' b/');
|
|
101
|
+
if (rest.startsWith('a/') && middle !== -1) {
|
|
102
|
+
for (let at = middle; at !== -1; at = rest.indexOf(' b/', at + 1)) {
|
|
103
|
+
if (rest.slice(2, at) === rest.slice(at + 3)) {
|
|
104
|
+
return {
|
|
105
|
+
oldPath: rest.slice(2, at),
|
|
106
|
+
newPath: rest.slice(at + 3),
|
|
107
|
+
prefixed: true
|
|
108
|
+
};
|
|
109
|
+
}
|
|
69
110
|
}
|
|
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
111
|
return {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
|
112
|
+
oldPath: rest.slice(2, middle),
|
|
113
|
+
newPath: unquote(rest.slice(middle + 3)),
|
|
114
|
+
prefixed: true
|
|
92
115
|
};
|
|
116
|
+
}
|
|
117
|
+
const split = rest.indexOf(' ');
|
|
118
|
+
return split === -1 ? {
|
|
119
|
+
oldPath: unquote(rest),
|
|
120
|
+
newPath: null,
|
|
121
|
+
prefixed: false
|
|
122
|
+
} : {
|
|
123
|
+
oldPath: unquote(rest.slice(0, split)),
|
|
124
|
+
newPath: unquote(rest.slice(split + 1)),
|
|
125
|
+
prefixed: false
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function emptySection(firstLine, git) {
|
|
129
|
+
return {
|
|
130
|
+
oldPath: null,
|
|
131
|
+
newPath: null,
|
|
132
|
+
git,
|
|
133
|
+
created: false,
|
|
134
|
+
deleted: false,
|
|
135
|
+
renamed: false,
|
|
136
|
+
copied: false,
|
|
137
|
+
binary: false,
|
|
138
|
+
firstLine,
|
|
139
|
+
lastLine: firstLine,
|
|
140
|
+
added: 0,
|
|
141
|
+
removed: 0,
|
|
142
|
+
hunks: [],
|
|
143
|
+
named: false,
|
|
144
|
+
prefixed: false
|
|
145
|
+
};
|
|
93
146
|
}
|
|
94
147
|
function statusOf(section) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
148
|
+
if (section.binary) {
|
|
149
|
+
return 'binary';
|
|
150
|
+
}
|
|
151
|
+
if (section.created || !section.git && section.oldPath === null && section.newPath !== null) {
|
|
152
|
+
return 'added';
|
|
153
|
+
}
|
|
154
|
+
if (section.deleted || !section.git && section.newPath === null && section.oldPath !== null) {
|
|
155
|
+
return 'deleted';
|
|
156
|
+
}
|
|
157
|
+
if (section.renamed) {
|
|
158
|
+
return 'renamed';
|
|
159
|
+
}
|
|
160
|
+
return section.copied ? 'copied' : 'modified';
|
|
108
161
|
}
|
|
109
162
|
function linesOf(text, limits) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
163
|
+
const lines = [];
|
|
164
|
+
let start = 0;
|
|
165
|
+
while (lines.length < limits.maxLines && start <= limits.maxCharacters) {
|
|
166
|
+
const end = text.indexOf('\n', start);
|
|
167
|
+
if (end === -1) {
|
|
168
|
+
lines.push(text.slice(start).replace(/\r$/, ''));
|
|
169
|
+
return {
|
|
170
|
+
lines,
|
|
171
|
+
truncated: false
|
|
172
|
+
};
|
|
120
173
|
}
|
|
121
|
-
|
|
122
|
-
|
|
174
|
+
lines.push(text.slice(start, end).replace(/\r$/, ''));
|
|
175
|
+
start = end + 1;
|
|
176
|
+
}
|
|
177
|
+
// Stopping right after a final newline leaves nothing unread.
|
|
178
|
+
return {
|
|
179
|
+
lines,
|
|
180
|
+
truncated: start < text.length
|
|
181
|
+
};
|
|
123
182
|
}
|
|
124
183
|
/*
|
|
125
184
|
* What every line of a patch is, with the file numbers of its lines and the files it touches. It reads
|
|
@@ -128,177 +187,187 @@ function linesOf(text, limits) {
|
|
|
128
187
|
* and says so in `truncated`.
|
|
129
188
|
*/
|
|
130
189
|
export function patchLayoutOf(text, limits = {}) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
}
|
|
190
|
+
const {
|
|
191
|
+
lines: source,
|
|
192
|
+
truncated
|
|
193
|
+
} = linesOf(text, {
|
|
194
|
+
...DEFAULT_PATCH_LIMITS,
|
|
195
|
+
...limits
|
|
196
|
+
});
|
|
197
|
+
const lines = [];
|
|
198
|
+
const sections = [];
|
|
199
|
+
let section;
|
|
200
|
+
let hunk;
|
|
201
|
+
let binaryData = false;
|
|
202
|
+
const seal = () => {
|
|
203
|
+
if (hunk !== undefined && (hunk.oldLeft > 0 || hunk.newLeft > 0)) {
|
|
204
|
+
hunk.layout.consistent = false;
|
|
228
205
|
}
|
|
206
|
+
hunk = undefined;
|
|
207
|
+
};
|
|
208
|
+
const open = (line, git) => {
|
|
229
209
|
seal();
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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;
|
|
210
|
+
binaryData = false;
|
|
211
|
+
section = emptySection(line, git);
|
|
212
|
+
sections.push(section);
|
|
213
|
+
return section;
|
|
214
|
+
};
|
|
215
|
+
// A removed line can read `-- x`, so `---` starts a file only with `+++` under it.
|
|
216
|
+
const startsFile = index => source[index].startsWith('--- ') && (source[index + 1]?.startsWith('+++ ') ?? false);
|
|
217
|
+
const push = (line, index) => {
|
|
218
|
+
lines.push(line);
|
|
219
|
+
if (section !== undefined) {
|
|
220
|
+
section.lastLine = index;
|
|
252
221
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const
|
|
256
|
-
|
|
222
|
+
};
|
|
223
|
+
for (let index = 0; index < source.length; index++) {
|
|
224
|
+
const line = source[index];
|
|
225
|
+
const remaining = hunk !== undefined && (hunk.oldLeft > 0 || hunk.newLeft > 0);
|
|
226
|
+
const gitStart = line.startsWith('diff --git ');
|
|
227
|
+
const header = line.startsWith('@@') ? parseHunkHeader(line) : undefined;
|
|
228
|
+
const fileStart = startsFile(index) && (!remaining || source[index + 2]?.startsWith('@@') === true);
|
|
229
|
+
if (remaining && !gitStart && header === undefined && !fileStart) {
|
|
230
|
+
if (isContent(line) || line === '' || line.startsWith('\\')) {
|
|
231
|
+
push(contentLine(line, hunk, section), index);
|
|
232
|
+
} else {
|
|
257
233
|
hunk.layout.consistent = false;
|
|
234
|
+
push(OTHER, index);
|
|
235
|
+
}
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (gitStart) {
|
|
239
|
+
const created = open(index, true);
|
|
240
|
+
({
|
|
241
|
+
oldPath: created.oldPath,
|
|
242
|
+
newPath: created.newPath,
|
|
243
|
+
prefixed: created.prefixed
|
|
244
|
+
} = gitPaths(line.slice('diff --git '.length)));
|
|
245
|
+
push(FILE_HEADER, index);
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (fileStart) {
|
|
249
|
+
const reuse = section !== undefined && section.git && !section.named && section.hunks.length === 0;
|
|
250
|
+
const target = reuse ? section : open(index, false);
|
|
251
|
+
seal();
|
|
252
|
+
nameSides(target, line, source[index + 1]);
|
|
253
|
+
push(FILE_HEADER, index);
|
|
254
|
+
push(FILE_HEADER, index + 1);
|
|
255
|
+
index++;
|
|
256
|
+
continue;
|
|
258
257
|
}
|
|
259
|
-
if (
|
|
260
|
-
|
|
258
|
+
if (header !== undefined) {
|
|
259
|
+
seal();
|
|
260
|
+
const target = section ?? open(index, false);
|
|
261
|
+
const layout = {
|
|
262
|
+
line: index,
|
|
263
|
+
...header,
|
|
264
|
+
consistent: true
|
|
265
|
+
};
|
|
266
|
+
target.hunks.push(layout);
|
|
267
|
+
hunk = {
|
|
268
|
+
layout,
|
|
269
|
+
oldLeft: header.oldCount,
|
|
270
|
+
newLeft: header.newCount,
|
|
271
|
+
oldNumber: header.oldStart,
|
|
272
|
+
newNumber: header.newStart
|
|
273
|
+
};
|
|
274
|
+
push(HUNK_HEADER, index);
|
|
275
|
+
continue;
|
|
261
276
|
}
|
|
262
|
-
|
|
263
|
-
|
|
277
|
+
if (section === undefined) {
|
|
278
|
+
push(OTHER, index);
|
|
279
|
+
} else if (hunk !== undefined && line.startsWith('\\')) {
|
|
280
|
+
push(NO_NEWLINE, index);
|
|
281
|
+
} else if (hunk !== undefined && isContent(line) && line !== '-- ') {
|
|
282
|
+
// The counts ran out, so a line a person typed below the hunk extends it. The `-- ` of a mail signature does not.
|
|
283
|
+
hunk.layout.consistent = false;
|
|
284
|
+
push(contentLine(line, hunk, section), index);
|
|
285
|
+
} else if (hunk === undefined && section.git && GIT_HEADERS.test(line)) {
|
|
286
|
+
readGitHeader(section, line);
|
|
287
|
+
push(FILE_HEADER, index);
|
|
288
|
+
} else if (/^Binary files .* differ$/.test(line) || line === 'GIT binary patch') {
|
|
289
|
+
section.binary = true;
|
|
290
|
+
binaryData = line === 'GIT binary patch';
|
|
291
|
+
push(BINARY, index);
|
|
292
|
+
} else if (binaryData && line !== '') {
|
|
293
|
+
push(BINARY, index);
|
|
294
|
+
} else if (line === '') {
|
|
295
|
+
lines.push(OTHER);
|
|
296
|
+
} else {
|
|
297
|
+
push(OTHER, index);
|
|
264
298
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
299
|
+
}
|
|
300
|
+
seal();
|
|
301
|
+
return {
|
|
302
|
+
lines,
|
|
303
|
+
truncated,
|
|
304
|
+
files: sections.map(entry => ({
|
|
305
|
+
oldPath: entry.oldPath,
|
|
306
|
+
newPath: entry.newPath,
|
|
307
|
+
status: statusOf(entry),
|
|
308
|
+
firstLine: entry.firstLine,
|
|
309
|
+
lastLine: entry.lastLine,
|
|
310
|
+
added: entry.added,
|
|
311
|
+
removed: entry.removed,
|
|
312
|
+
hunks: entry.hunks
|
|
313
|
+
}))
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
function isContent(line) {
|
|
317
|
+
return line.startsWith('+') || line.startsWith('-') || line.startsWith(' ');
|
|
318
|
+
}
|
|
319
|
+
/* A line under a hunk header; an empty line is a context line whose space an editor trimmed. */
|
|
320
|
+
function contentLine(line, hunk, section) {
|
|
321
|
+
if (line.startsWith('\\')) {
|
|
322
|
+
return NO_NEWLINE;
|
|
323
|
+
}
|
|
324
|
+
const kind = line.startsWith('+') ? 'added' : line.startsWith('-') ? 'removed' : 'context';
|
|
325
|
+
const takesOld = kind !== 'added';
|
|
326
|
+
const takesNew = kind !== 'removed';
|
|
327
|
+
if (takesOld && hunk.oldLeft === 0 || takesNew && hunk.newLeft === 0) {
|
|
328
|
+
hunk.layout.consistent = false;
|
|
329
|
+
}
|
|
330
|
+
if (kind === 'added') {
|
|
331
|
+
section.added++;
|
|
332
|
+
} else if (kind === 'removed') {
|
|
333
|
+
section.removed++;
|
|
334
|
+
}
|
|
335
|
+
const oldNumber = takesOld ? hunk.oldNumber++ : null;
|
|
336
|
+
const newNumber = takesNew ? hunk.newNumber++ : null;
|
|
337
|
+
hunk.oldLeft = takesOld ? Math.max(0, hunk.oldLeft - 1) : hunk.oldLeft;
|
|
338
|
+
hunk.newLeft = takesNew ? Math.max(0, hunk.newLeft - 1) : hunk.newLeft;
|
|
339
|
+
return {
|
|
340
|
+
role: kind,
|
|
341
|
+
old: oldNumber,
|
|
342
|
+
new: newNumber
|
|
343
|
+
};
|
|
270
344
|
}
|
|
271
345
|
function nameSides(section, oldLine, newLine) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
346
|
+
const oldName = pathOf(oldLine.slice(4));
|
|
347
|
+
const newName = pathOf(newLine.slice(4));
|
|
348
|
+
const prefixed = section.git ? section.prefixed : (oldName?.startsWith('a/') ?? false) && (newName?.startsWith('b/') ?? false);
|
|
349
|
+
section.oldPath = prefixed ? stripPrefix(oldName, 'a/') : oldName;
|
|
350
|
+
section.newPath = prefixed ? stripPrefix(newName, 'b/') : newName;
|
|
351
|
+
section.created ||= oldName === null;
|
|
352
|
+
section.deleted ||= newName === null;
|
|
353
|
+
section.named = true;
|
|
280
354
|
}
|
|
281
355
|
function readGitHeader(section, line) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
else if (line.startsWith('copy to ')) {
|
|
301
|
-
section.copied = true;
|
|
302
|
-
section.newPath = pathOf(line.slice('copy to '.length));
|
|
303
|
-
}
|
|
304
|
-
}
|
|
356
|
+
if (line.startsWith('new file mode ')) {
|
|
357
|
+
section.created = true;
|
|
358
|
+
} else if (line.startsWith('deleted file mode ')) {
|
|
359
|
+
section.deleted = true;
|
|
360
|
+
} else if (line.startsWith('rename from ')) {
|
|
361
|
+
section.renamed = true;
|
|
362
|
+
section.oldPath = pathOf(line.slice('rename from '.length));
|
|
363
|
+
} else if (line.startsWith('rename to ')) {
|
|
364
|
+
section.renamed = true;
|
|
365
|
+
section.newPath = pathOf(line.slice('rename to '.length));
|
|
366
|
+
} else if (line.startsWith('copy from ')) {
|
|
367
|
+
section.copied = true;
|
|
368
|
+
section.oldPath = pathOf(line.slice('copy from '.length));
|
|
369
|
+
} else if (line.startsWith('copy to ')) {
|
|
370
|
+
section.copied = true;
|
|
371
|
+
section.newPath = pathOf(line.slice('copy to '.length));
|
|
372
|
+
}
|
|
373
|
+
}
|
package/dist/paths.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* A path inside `folder` relative to it; any other path as it is. */
|
|
2
2
|
export function relativeTo(path, folder) {
|
|
3
|
-
|
|
3
|
+
return path.startsWith(`${folder}/`) ? path.slice(folder.length + 1) : path;
|
|
4
4
|
}
|
|
5
5
|
export function basenameOf(path) {
|
|
6
|
-
|
|
7
|
-
}
|
|
6
|
+
return path.replaceAll('\\', '/').split('/').at(-1) ?? path;
|
|
7
|
+
}
|