@design.estate/dees-catalog 13.1.0 → 13.2.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_bundle/bundle.js +453 -150
- package/dist_bundle/bundle.js.map +1 -1
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/00group-chart/dees-chart-area/component.d.ts +2 -0
- package/dist_ts_web/elements/00group-chart/dees-chart-area/component.js +11 -2
- package/dist_ts_web/elements/00group-chart/dees-chart-area/styles.js +4 -1
- package/dist_ts_web/elements/00group-chart/dees-chart-area/template.js +12 -8
- package/dist_ts_web/elements/00group-dataview/dees-dataview-codebox/codebox.diff.d.ts +37 -1
- package/dist_ts_web/elements/00group-dataview/dees-dataview-codebox/codebox.diff.js +267 -79
- package/dist_ts_web/elements/00group-dataview/dees-dataview-codebox/dees-dataview-codebox.d.ts +7 -0
- package/dist_ts_web/elements/00group-dataview/dees-dataview-codebox/dees-dataview-codebox.js +192 -40
- package/package.json +1 -1
- package/readme.md +19 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/00group-chart/dees-chart-area/component.ts +4 -0
- package/ts_web/elements/00group-chart/dees-chart-area/styles.ts +3 -0
- package/ts_web/elements/00group-chart/dees-chart-area/template.ts +11 -7
- package/ts_web/elements/00group-dataview/dees-dataview-codebox/codebox.diff.ts +309 -76
- package/ts_web/elements/00group-dataview/dees-dataview-codebox/dees-dataview-codebox.ts +184 -37
|
@@ -61,6 +61,44 @@ export type TUnifiedDiffSummary =
|
|
|
61
61
|
| { type: 'binary' }
|
|
62
62
|
| { type: 'unparseable' };
|
|
63
63
|
|
|
64
|
+
export type TUnifiedDiffMetadataKind =
|
|
65
|
+
| 'index'
|
|
66
|
+
| 'new-file-mode'
|
|
67
|
+
| 'deleted-file-mode'
|
|
68
|
+
| 'old-mode'
|
|
69
|
+
| 'new-mode'
|
|
70
|
+
| 'similarity-index'
|
|
71
|
+
| 'dissimilarity-index'
|
|
72
|
+
| 'rename-from'
|
|
73
|
+
| 'rename-to'
|
|
74
|
+
| 'copy-from'
|
|
75
|
+
| 'copy-to';
|
|
76
|
+
|
|
77
|
+
export interface IUnifiedDiffMetadata {
|
|
78
|
+
kind: TUnifiedDiffMetadataKind;
|
|
79
|
+
value: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** One ordered file section from a unified patch. Paths never retain Git's a/ and b/ prefixes. */
|
|
83
|
+
export interface IUnifiedDiffFileSection {
|
|
84
|
+
beforePath?: string;
|
|
85
|
+
afterPath?: string;
|
|
86
|
+
displayPath?: string;
|
|
87
|
+
metadata: IUnifiedDiffMetadata[];
|
|
88
|
+
rows: IDiffRow[];
|
|
89
|
+
binary: boolean;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export type TUnifiedDiffDocumentSummary =
|
|
93
|
+
| { type: 'exact'; stats?: IDiffStats; binaryFiles: number }
|
|
94
|
+
| { type: 'partial'; stats?: IDiffStats; binaryFiles: number }
|
|
95
|
+
| { type: 'unparseable'; binaryFiles: number };
|
|
96
|
+
|
|
97
|
+
export interface IUnifiedDiffDocument {
|
|
98
|
+
files: IUnifiedDiffFileSection[];
|
|
99
|
+
summary: TUnifiedDiffDocumentSummary;
|
|
100
|
+
}
|
|
101
|
+
|
|
64
102
|
const splitLines = (text: string): string[] => {
|
|
65
103
|
if (!text) return [];
|
|
66
104
|
const lines = text.split('\n');
|
|
@@ -171,61 +209,157 @@ export const computeLineDiff = (beforeText: string, afterText: string): IDiffRow
|
|
|
171
209
|
return annotateIntraline(rows);
|
|
172
210
|
};
|
|
173
211
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
for (
|
|
183
|
-
const
|
|
184
|
-
if (
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
remainingNew = hunkMatch[4] === undefined ? 1 : Number(hunkMatch[4]);
|
|
189
|
-
inHunk = true;
|
|
190
|
-
rows.push({ kind: 'hunk', text: line });
|
|
212
|
+
const decodeGitQuotedPath = (valueArg: string): string => {
|
|
213
|
+
if (!(valueArg.startsWith('"') && valueArg.endsWith('"'))) return valueArg;
|
|
214
|
+
const source = valueArg.slice(1, -1);
|
|
215
|
+
const bytes: number[] = [];
|
|
216
|
+
const encoder = new TextEncoder();
|
|
217
|
+
const escapeBytes: Record<string, number> = {
|
|
218
|
+
a: 7, b: 8, t: 9, n: 10, v: 11, f: 12, r: 13, '"': 34, '\\': 92,
|
|
219
|
+
};
|
|
220
|
+
for (let index = 0; index < source.length; index++) {
|
|
221
|
+
const character = source[index]!;
|
|
222
|
+
if (character !== '\\') {
|
|
223
|
+
const codePoint = source.codePointAt(index)!;
|
|
224
|
+
bytes.push(...encoder.encode(String.fromCodePoint(codePoint)));
|
|
225
|
+
if (codePoint > 0xffff) index++;
|
|
191
226
|
continue;
|
|
192
227
|
}
|
|
193
|
-
|
|
194
|
-
if (
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
remainingNew--;
|
|
198
|
-
} else if (line.startsWith('-')) {
|
|
199
|
-
rows.push({ kind: 'remove', text: line.slice(1), oldLine: oldLine++ });
|
|
200
|
-
remainingOld--;
|
|
201
|
-
} else {
|
|
202
|
-
rows.push({ kind: 'context', text: line.startsWith(' ') ? line.slice(1) : line, oldLine: oldLine++, newLine: newLine++ });
|
|
203
|
-
remainingOld--;
|
|
204
|
-
remainingNew--;
|
|
228
|
+
const escaped = source[++index];
|
|
229
|
+
if (escaped === undefined) {
|
|
230
|
+
bytes.push(92);
|
|
231
|
+
break;
|
|
205
232
|
}
|
|
206
|
-
|
|
233
|
+
const octal = /^[0-7]{3}$/.exec(source.slice(index, index + 3));
|
|
234
|
+
if (octal) {
|
|
235
|
+
bytes.push(Number.parseInt(octal[0], 8));
|
|
236
|
+
index += 2;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
bytes.push(escapeBytes[escaped] ?? escaped.charCodeAt(0));
|
|
207
240
|
}
|
|
208
|
-
return
|
|
241
|
+
return new TextDecoder().decode(Uint8Array.from(bytes));
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const readGitPathToken = (
|
|
245
|
+
sourceArg: string,
|
|
246
|
+
startArg = 0,
|
|
247
|
+
): { value: string; end: number } | undefined => {
|
|
248
|
+
let start = startArg;
|
|
249
|
+
while (sourceArg[start] === ' ') start++;
|
|
250
|
+
if (start >= sourceArg.length) return undefined;
|
|
251
|
+
if (sourceArg[start] !== '"') return { value: sourceArg.slice(start), end: sourceArg.length };
|
|
252
|
+
let escaped = false;
|
|
253
|
+
for (let index = start + 1; index < sourceArg.length; index++) {
|
|
254
|
+
const character = sourceArg[index]!;
|
|
255
|
+
if (character === '"' && !escaped) {
|
|
256
|
+
return {
|
|
257
|
+
value: decodeGitQuotedPath(sourceArg.slice(start, index + 1)),
|
|
258
|
+
end: index + 1,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
escaped = character === '\\' && !escaped;
|
|
262
|
+
if (character !== '\\') escaped = false;
|
|
263
|
+
}
|
|
264
|
+
return undefined;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
const normalizeGitPath = (valueArg: string | undefined): string | undefined => {
|
|
268
|
+
if (!valueArg) return undefined;
|
|
269
|
+
const value = decodeGitQuotedPath(valueArg);
|
|
270
|
+
if (value === '/dev/null') return undefined;
|
|
271
|
+
return value.startsWith('a/') || value.startsWith('b/') ? value.slice(2) : value;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const parsePatchHeaderPath = (valueArg: string): string | undefined => {
|
|
275
|
+
const value = valueArg.startsWith('"')
|
|
276
|
+
? readGitPathToken(valueArg)?.value
|
|
277
|
+
: valueArg.split('\t', 1)[0];
|
|
278
|
+
return normalizeGitPath(value);
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const parseMetadataPath = (valueArg: string): string | undefined => {
|
|
282
|
+
const value = valueArg.startsWith('"')
|
|
283
|
+
? readGitPathToken(valueArg)?.value
|
|
284
|
+
: valueArg;
|
|
285
|
+
return value === '/dev/null' ? undefined : value;
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const parseDiffGitPaths = (lineArg: string): { beforePath?: string; afterPath?: string } => {
|
|
289
|
+
const value = lineArg.slice('diff --git '.length);
|
|
290
|
+
if (value.startsWith('"')) {
|
|
291
|
+
const before = readGitPathToken(value);
|
|
292
|
+
const after = before ? readGitPathToken(value, before.end) : undefined;
|
|
293
|
+
const beforePath = normalizeGitPath(before?.value);
|
|
294
|
+
const afterPath = normalizeGitPath(after?.value);
|
|
295
|
+
return {
|
|
296
|
+
...(beforePath ? { beforePath } : {}),
|
|
297
|
+
...(afterPath ? { afterPath } : {}),
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
const boundary = Math.max(value.lastIndexOf(' b/'), value.lastIndexOf(' "b/'));
|
|
301
|
+
if (boundary < 0) return {};
|
|
302
|
+
const beforePath = normalizeGitPath(value.slice(0, boundary));
|
|
303
|
+
const afterToken = value.slice(boundary + 1);
|
|
304
|
+
const afterPath = normalizeGitPath(afterToken.startsWith('"')
|
|
305
|
+
? readGitPathToken(afterToken)?.value
|
|
306
|
+
: afterToken);
|
|
307
|
+
return {
|
|
308
|
+
...(beforePath ? { beforePath } : {}),
|
|
309
|
+
...(afterPath ? { afterPath } : {}),
|
|
310
|
+
};
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
const metadataLine = (lineArg: string): IUnifiedDiffMetadata | undefined => {
|
|
314
|
+
const kinds: Array<[TUnifiedDiffMetadataKind, string]> = [
|
|
315
|
+
['new-file-mode', 'new file mode '],
|
|
316
|
+
['deleted-file-mode', 'deleted file mode '],
|
|
317
|
+
['old-mode', 'old mode '],
|
|
318
|
+
['new-mode', 'new mode '],
|
|
319
|
+
['similarity-index', 'similarity index '],
|
|
320
|
+
['dissimilarity-index', 'dissimilarity index '],
|
|
321
|
+
['rename-from', 'rename from '],
|
|
322
|
+
['rename-to', 'rename to '],
|
|
323
|
+
['copy-from', 'copy from '],
|
|
324
|
+
['copy-to', 'copy to '],
|
|
325
|
+
['index', 'index '],
|
|
326
|
+
];
|
|
327
|
+
const match = kinds.find(([, prefix]) => lineArg.startsWith(prefix));
|
|
328
|
+
if (!match) return undefined;
|
|
329
|
+
const rawValue = lineArg.slice(match[1].length);
|
|
330
|
+
const pathMetadata = match[0] === 'rename-from'
|
|
331
|
+
|| match[0] === 'rename-to'
|
|
332
|
+
|| match[0] === 'copy-from'
|
|
333
|
+
|| match[0] === 'copy-to';
|
|
334
|
+
return {
|
|
335
|
+
kind: match[0],
|
|
336
|
+
value: pathMetadata ? parseMetadataPath(rawValue) ?? rawValue : rawValue,
|
|
337
|
+
};
|
|
209
338
|
};
|
|
210
339
|
|
|
211
340
|
/**
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
* reports
|
|
341
|
+
* Parses one unified patch without discarding file boundaries or extended Git metadata.
|
|
342
|
+
* Only complete hunks contribute to summary counts. A producer-declared truncation therefore
|
|
343
|
+
* reports a trustworthy complete prefix instead of inventing totals for the missing suffix.
|
|
215
344
|
*/
|
|
216
|
-
export const
|
|
345
|
+
export const parseUnifiedDiffDocument = (
|
|
217
346
|
patchText: string,
|
|
218
347
|
options: IUnifiedDiffSummaryOptions = {},
|
|
219
|
-
):
|
|
220
|
-
const
|
|
221
|
-
let
|
|
222
|
-
let
|
|
223
|
-
let
|
|
224
|
-
let
|
|
348
|
+
): IUnifiedDiffDocument => {
|
|
349
|
+
const files: IUnifiedDiffFileSection[] = [];
|
|
350
|
+
let current: IUnifiedDiffFileSection | undefined;
|
|
351
|
+
let currentSawOldHeader = false;
|
|
352
|
+
let currentSawNewHeader = false;
|
|
353
|
+
let sawRecognizedContent = false;
|
|
354
|
+
let sawHeaderPair = false;
|
|
355
|
+
let unparseable = false;
|
|
356
|
+
let binaryPayload = false;
|
|
225
357
|
let added = 0;
|
|
226
358
|
let removed = 0;
|
|
227
359
|
let completeHunks = 0;
|
|
228
360
|
let hunk: {
|
|
361
|
+
oldLine: number;
|
|
362
|
+
newLine: number;
|
|
229
363
|
expectedOld: number;
|
|
230
364
|
expectedNew: number;
|
|
231
365
|
observedOld: number;
|
|
@@ -234,9 +368,28 @@ export const summarizeUnifiedDiff = (
|
|
|
234
368
|
removed: number;
|
|
235
369
|
} | undefined;
|
|
236
370
|
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
371
|
+
const createFile = (pathsArg: { beforePath?: string; afterPath?: string } = {}) => {
|
|
372
|
+
current = {
|
|
373
|
+
...pathsArg,
|
|
374
|
+
displayPath: pathsArg.afterPath ?? pathsArg.beforePath,
|
|
375
|
+
metadata: [],
|
|
376
|
+
rows: [],
|
|
377
|
+
binary: false,
|
|
378
|
+
};
|
|
379
|
+
files.push(current);
|
|
380
|
+
currentSawOldHeader = false;
|
|
381
|
+
currentSawNewHeader = false;
|
|
382
|
+
binaryPayload = false;
|
|
383
|
+
return current;
|
|
384
|
+
};
|
|
385
|
+
const requireFile = () => current ?? createFile();
|
|
386
|
+
const refreshDisplayPath = (fileArg: IUnifiedDiffFileSection) => {
|
|
387
|
+
fileArg.displayPath = fileArg.afterPath ?? fileArg.beforePath;
|
|
388
|
+
};
|
|
389
|
+
const completeHunk = () => {
|
|
390
|
+
if (!hunk
|
|
391
|
+
|| hunk.observedOld !== hunk.expectedOld
|
|
392
|
+
|| hunk.observedNew !== hunk.expectedNew) return false;
|
|
240
393
|
added += hunk.added;
|
|
241
394
|
removed += hunk.removed;
|
|
242
395
|
completeHunks++;
|
|
@@ -244,13 +397,23 @@ export const summarizeUnifiedDiff = (
|
|
|
244
397
|
return true;
|
|
245
398
|
};
|
|
246
399
|
|
|
247
|
-
for (const line of
|
|
248
|
-
|
|
400
|
+
for (const line of splitLines(patchText)) {
|
|
401
|
+
if (line.startsWith('diff --git ')) {
|
|
402
|
+
if (hunk) {
|
|
403
|
+
unparseable = true;
|
|
404
|
+
hunk = undefined;
|
|
405
|
+
}
|
|
406
|
+
createFile(parseDiffGitPaths(line));
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const hunkMatch = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(.*)$/.exec(line);
|
|
249
411
|
if (hunkMatch) {
|
|
250
|
-
if (
|
|
251
|
-
|
|
252
|
-
sawHunk = true;
|
|
412
|
+
if (hunk) unparseable = true;
|
|
413
|
+
const file = requireFile();
|
|
253
414
|
hunk = {
|
|
415
|
+
oldLine: Number(hunkMatch[1]),
|
|
416
|
+
newLine: Number(hunkMatch[3]),
|
|
254
417
|
expectedOld: hunkMatch[2] === undefined ? 1 : Number(hunkMatch[2]),
|
|
255
418
|
expectedNew: hunkMatch[4] === undefined ? 1 : Number(hunkMatch[4]),
|
|
256
419
|
observedOld: 0,
|
|
@@ -258,65 +421,135 @@ export const summarizeUnifiedDiff = (
|
|
|
258
421
|
added: 0,
|
|
259
422
|
removed: 0,
|
|
260
423
|
};
|
|
261
|
-
|
|
424
|
+
file.rows.push({ kind: 'hunk', text: line });
|
|
425
|
+
sawRecognizedContent = true;
|
|
426
|
+
completeHunk();
|
|
262
427
|
continue;
|
|
263
428
|
}
|
|
264
429
|
|
|
265
|
-
if (/^(Binary files .+ differ|GIT binary patch)$/.test(line)) return { type: 'binary' };
|
|
266
430
|
if (line.startsWith('\')) continue;
|
|
267
|
-
|
|
268
431
|
if (hunk) {
|
|
432
|
+
const file = requireFile();
|
|
269
433
|
if (line.startsWith('+')) {
|
|
434
|
+
file.rows.push({ kind: 'add', text: line.slice(1), newLine: hunk.newLine++ });
|
|
270
435
|
hunk.observedNew++;
|
|
271
436
|
hunk.added++;
|
|
272
437
|
} else if (line.startsWith('-')) {
|
|
438
|
+
file.rows.push({ kind: 'remove', text: line.slice(1), oldLine: hunk.oldLine++ });
|
|
273
439
|
hunk.observedOld++;
|
|
274
440
|
hunk.removed++;
|
|
275
441
|
} else if (line.startsWith(' ')) {
|
|
442
|
+
file.rows.push({
|
|
443
|
+
kind: 'context',
|
|
444
|
+
text: line.slice(1),
|
|
445
|
+
oldLine: hunk.oldLine++,
|
|
446
|
+
newLine: hunk.newLine++,
|
|
447
|
+
});
|
|
276
448
|
hunk.observedOld++;
|
|
277
449
|
hunk.observedNew++;
|
|
278
450
|
} else {
|
|
279
|
-
|
|
451
|
+
unparseable = true;
|
|
452
|
+
hunk = undefined;
|
|
453
|
+
continue;
|
|
280
454
|
}
|
|
281
|
-
if (hunk
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
455
|
+
if (hunk
|
|
456
|
+
&& (hunk.observedOld > hunk.expectedOld || hunk.observedNew > hunk.expectedNew)) {
|
|
457
|
+
unparseable = true;
|
|
458
|
+
hunk = undefined;
|
|
459
|
+
} else {
|
|
460
|
+
completeHunk();
|
|
286
461
|
}
|
|
287
462
|
continue;
|
|
288
463
|
}
|
|
289
464
|
|
|
465
|
+
if (binaryPayload) continue;
|
|
290
466
|
if (line.startsWith('--- ')) {
|
|
291
|
-
|
|
292
|
-
|
|
467
|
+
if (currentSawNewHeader) createFile();
|
|
468
|
+
const file = requireFile();
|
|
469
|
+
file.beforePath = parsePatchHeaderPath(line.slice(4));
|
|
470
|
+
currentSawOldHeader = true;
|
|
471
|
+
refreshDisplayPath(file);
|
|
293
472
|
continue;
|
|
294
473
|
}
|
|
295
474
|
if (line.startsWith('+++ ')) {
|
|
296
|
-
|
|
297
|
-
|
|
475
|
+
const file = requireFile();
|
|
476
|
+
file.afterPath = parsePatchHeaderPath(line.slice(4));
|
|
477
|
+
currentSawNewHeader = true;
|
|
478
|
+
sawHeaderPair ||= currentSawOldHeader;
|
|
479
|
+
refreshDisplayPath(file);
|
|
298
480
|
continue;
|
|
299
481
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
482
|
+
const metadata = metadataLine(line);
|
|
483
|
+
if (metadata) {
|
|
484
|
+
const file = requireFile();
|
|
485
|
+
file.metadata.push(metadata);
|
|
486
|
+
if (metadata.kind === 'rename-from' || metadata.kind === 'copy-from') {
|
|
487
|
+
file.beforePath ??= metadata.value;
|
|
488
|
+
}
|
|
489
|
+
if (metadata.kind === 'rename-to' || metadata.kind === 'copy-to') {
|
|
490
|
+
file.afterPath ??= metadata.value;
|
|
491
|
+
}
|
|
492
|
+
refreshDisplayPath(file);
|
|
493
|
+
sawRecognizedContent = true;
|
|
306
494
|
continue;
|
|
307
495
|
}
|
|
308
|
-
|
|
496
|
+
const binaryMatch = /^Binary files (.+) and (.+) differ$/.exec(line);
|
|
497
|
+
if (binaryMatch) {
|
|
498
|
+
const file = requireFile();
|
|
499
|
+
file.beforePath ??= parsePatchHeaderPath(binaryMatch[1]!);
|
|
500
|
+
file.afterPath ??= parsePatchHeaderPath(binaryMatch[2]!);
|
|
501
|
+
refreshDisplayPath(file);
|
|
502
|
+
file.binary = true;
|
|
503
|
+
sawRecognizedContent = true;
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
if (line === 'GIT binary patch') {
|
|
507
|
+
requireFile().binary = true;
|
|
508
|
+
binaryPayload = true;
|
|
509
|
+
sawRecognizedContent = true;
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
if (line.trim()) unparseable = true;
|
|
309
513
|
}
|
|
310
514
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
return completeHunks ? { type: 'partial', stats: { added, removed } } : { type: 'partial' };
|
|
515
|
+
if (hunk) {
|
|
516
|
+
if (!options.truncated) unparseable = true;
|
|
517
|
+
hunk = undefined;
|
|
315
518
|
}
|
|
316
|
-
|
|
317
|
-
|
|
519
|
+
for (const file of files) file.rows = annotateIntraline(file.rows);
|
|
520
|
+
const binaryFiles = files.filter((file) => file.binary).length;
|
|
521
|
+
const stats = completeHunks > 0 ? { added, removed } : undefined;
|
|
522
|
+
const recognized = sawRecognizedContent || sawHeaderPair || binaryFiles > 0;
|
|
523
|
+
const summary: TUnifiedDiffDocumentSummary = unparseable || !recognized
|
|
524
|
+
? { type: 'unparseable', binaryFiles }
|
|
525
|
+
: options.truncated
|
|
526
|
+
? { type: 'partial', ...(stats ? { stats } : {}), binaryFiles }
|
|
527
|
+
: { type: 'exact', ...(stats ? { stats } : {}), binaryFiles };
|
|
528
|
+
return { files, summary };
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
/** Compatibility row view. File boundaries and metadata are available from parseUnifiedDiffDocument. */
|
|
532
|
+
export const parseUnifiedDiff = (patchText: string): IDiffRow[] =>
|
|
533
|
+
parseUnifiedDiffDocument(patchText).files.flatMap((file) => file.rows);
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Returns trustworthy change counts for a unified patch. Declared hunk sizes
|
|
537
|
+
* are validated before their rows contribute to the result. A truncated patch
|
|
538
|
+
* reports only the complete hunk prefix, and omits stats when none is complete.
|
|
539
|
+
*/
|
|
540
|
+
export const summarizeUnifiedDiff = (
|
|
541
|
+
patchText: string,
|
|
542
|
+
options: IUnifiedDiffSummaryOptions = {},
|
|
543
|
+
): TUnifiedDiffSummary => {
|
|
544
|
+
const document = parseUnifiedDiffDocument(patchText, options);
|
|
545
|
+
if (document.summary.binaryFiles > 0) return { type: 'binary' };
|
|
546
|
+
if (document.summary.type === 'unparseable') return { type: 'unparseable' };
|
|
547
|
+
if (document.summary.type === 'partial') {
|
|
548
|
+
return document.summary.stats
|
|
549
|
+
? { type: 'partial', stats: document.summary.stats }
|
|
550
|
+
: { type: 'partial' };
|
|
318
551
|
}
|
|
319
|
-
return { type: 'exact', stats: { added, removed } };
|
|
552
|
+
return { type: 'exact', stats: document.summary.stats ?? { added: 0, removed: 0 } };
|
|
320
553
|
};
|
|
321
554
|
|
|
322
555
|
const maxIntralineChars = 400;
|