-
Notifications
You must be signed in to change notification settings - Fork 89
Expand file tree
/
Copy pathparse.ts
More file actions
101 lines (82 loc) · 3.31 KB
/
parse.ts
File metadata and controls
101 lines (82 loc) · 3.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import parser, {Change, DeleteChange, File, Hunk, InsertChange, NormalChange} from 'gitdiff-parser';
export function isInsert(change: Change): change is InsertChange {
return change.type === 'insert';
}
export function isDelete(change: Change): change is DeleteChange {
return change.type === 'delete';
}
export function isNormal(change: Change): change is NormalChange {
return change.type === 'normal';
}
export type {File as FileData, Hunk as HunkData, Change as ChangeData};
export interface ParseOptions {
nearbySequences?: 'zip';
}
function zipChanges(changes: Change[]) {
const [result] = changes.reduce<[Change[], Change | null, number]>(
([result, last, lastDeletionIndex], current, i) => {
if (!last) {
result.push(current);
return [result, current, isDelete(current) ? i : -1];
}
if (isInsert(current) && lastDeletionIndex >= 0) {
result.splice(lastDeletionIndex + 1, 0, current);
// The new `lastDeletionIndex` may be out of range, but `splice` will fix it
return [result, current, lastDeletionIndex + 2];
}
result.push(current);
// Keep the `lastDeletionIndex` if there are lines of deletions,
// otherwise update it to the new deletion line
const newLastDeletionIndex = isDelete(current) ? (isDelete(last) ? lastDeletionIndex : i) : i;
return [result, current, newLastDeletionIndex];
},
[[], null, -1]
);
return result;
}
function mapHunk(hunk: Hunk, options: ParseOptions) {
const changes = options.nearbySequences === 'zip' ? zipChanges(hunk.changes) : hunk.changes;
return {
...hunk,
isPlain: false,
changes: changes,
};
}
function mapFile(file: File, options: ParseOptions) {
const hunks = file.hunks.map(hunk => mapHunk(hunk, options));
return {...file, hunks};
}
function normalizeDiffText(text: string) {
// Git diff header:
//
// diff --git a/test/fixture/test/ci.go b/test/fixture/test/ci.go
// index 6829b8a2..4c565f1b 100644
// --- a/test/fixture/test/ci.go
// +++ b/test/fixture/test/ci.go
if (text.startsWith('diff --git')) {
return text;
}
// Unidiff header:
//
// --- /test/fixture/test/ci.go 2002-02-21 23:30:39.942229878 -0800
// +++ /test/fixture/test/ci.go 2002-02-21 23:30:50.442260588 -0800
const indexOfFirstLineBreak = text.indexOf('\n');
const indexOfSecondLineBreak = text.indexOf('\n', indexOfFirstLineBreak + 1);
const firstLine = text.slice(0, indexOfFirstLineBreak);
const secondLine = text.slice(indexOfFirstLineBreak + 1, indexOfSecondLineBreak);
const oldPath = firstLine.split(' ').slice(1, -3).join(' ');
const newPath = secondLine.split(' ').slice(1, -3).join(' ');
const segments = [
`diff --git a/${oldPath} b/${newPath}`,
'index 1111111..2222222 100644',
`--- a/${oldPath}`,
`+++ b/${newPath}`,
text.slice(indexOfSecondLineBreak + 1),
];
return segments.join('\n');
}
export function parseDiff(text: string, options: ParseOptions = {}): File[] {
const diffText = normalizeDiffText(text.trimStart());
const files = parser.parse(diffText);
return files.map(file => mapFile(file, options));
}