forked from golang/vscode-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
diffUtils.ts
153 lines (135 loc) · 4.33 KB
/
diffUtils.ts
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*---------------------------------------------------------
* Copyright (C) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------*/
import jsDiff = require('diff');
import { Position, Range, TextEditorEdit, Uri, WorkspaceEdit } from 'vscode';
import { getBinPathFromEnvVar } from './goPath';
let diffToolAvailable: boolean | null = null;
export function isDiffToolAvailable(): boolean {
if (diffToolAvailable == null) {
const envPath = process.env['PATH'] || (process.platform === 'win32' ? process.env['Path'] : null);
if (!envPath) {
return false;
}
diffToolAvailable = getBinPathFromEnvVar('diff', envPath, false) != null;
}
return diffToolAvailable;
}
export enum EditTypes {
EDIT_DELETE,
EDIT_INSERT,
EDIT_REPLACE
}
export class Edit {
public start: Position;
public end: Position;
public text: string;
private action: number;
constructor(action: number, start: Position) {
this.action = action;
this.start = start;
this.text = '';
}
// Applies Edit using given TextEditorEdit
public applyUsingTextEditorEdit(editBuilder: TextEditorEdit): void {
switch (this.action) {
case EditTypes.EDIT_INSERT:
editBuilder.insert(this.start, this.text);
break;
case EditTypes.EDIT_DELETE:
editBuilder.delete(new Range(this.start, this.end));
break;
case EditTypes.EDIT_REPLACE:
editBuilder.replace(new Range(this.start, this.end), this.text);
break;
}
}
// Applies Edits to given WorkspaceEdit
public applyUsingWorkspaceEdit(workspaceEdit: WorkspaceEdit, fileUri: Uri): void {
switch (this.action) {
case EditTypes.EDIT_INSERT:
workspaceEdit.insert(fileUri, this.start, this.text);
break;
case EditTypes.EDIT_DELETE:
workspaceEdit.delete(fileUri, new Range(this.start, this.end));
break;
case EditTypes.EDIT_REPLACE:
workspaceEdit.replace(fileUri, new Range(this.start, this.end), this.text);
break;
}
}
}
export interface FilePatch {
fileName: string;
edits: Edit[];
}
/**
* Uses diff module to parse given array of IUniDiff objects and returns edits for files
*
* @param diffOutput jsDiff.IUniDiff[]
*
* @returns Array of FilePatch objects, one for each file
*/
function parseUniDiffs(diffOutput: jsDiff.IUniDiff[]): FilePatch[] {
const filePatches: FilePatch[] = [];
diffOutput.forEach((uniDiff: jsDiff.IUniDiff) => {
let edit: Edit;
const edits: Edit[] = [];
uniDiff.hunks.forEach((hunk: jsDiff.IHunk) => {
let startLine = hunk.oldStart;
hunk.lines.forEach((line) => {
switch (line.substr(0, 1)) {
case '-':
edit = new Edit(EditTypes.EDIT_DELETE, new Position(startLine - 1, 0));
edit.end = new Position(startLine, 0);
edits.push(edit);
startLine++;
break;
case '+':
edit = new Edit(EditTypes.EDIT_INSERT, new Position(startLine - 1, 0));
edit.text += line.substr(1) + '\n';
edits.push(edit);
break;
case ' ':
startLine++;
break;
}
});
});
const fileName = uniDiff.oldFileName;
filePatches.push({ fileName, edits });
});
return filePatches;
}
/**
* Returns a FilePatch object by generating diffs between given oldStr and newStr using the diff module
*
* @param fileName string: Name of the file to which edits should be applied
* @param oldStr string
* @param newStr string
*
* @returns A single FilePatch object
*/
export function getEdits(fileName: string, oldStr: string, newStr: string): FilePatch {
if (process.platform === 'win32') {
oldStr = oldStr.split('\r\n').join('\n');
newStr = newStr.split('\r\n').join('\n');
}
const unifiedDiffs: jsDiff.IUniDiff = jsDiff.structuredPatch(fileName, fileName, oldStr, newStr, '', '');
const filePatches: FilePatch[] = parseUniDiffs([unifiedDiffs]);
return filePatches[0];
}
/**
* Uses diff module to parse given diff string and returns edits for files
*
* @param diffStr : Diff string in unified format.
* http://www.gnu.org/software/diffutils/manual/diffutils.html#Unified-Format
*
* @returns Array of FilePatch objects, one for each file
*/
export function getEditsFromUnifiedDiffStr(diffstr: string): FilePatch[] {
const unifiedDiffs: jsDiff.IUniDiff[] = jsDiff.parsePatch(diffstr);
const filePatches: FilePatch[] = parseUniDiffs(unifiedDiffs);
return filePatches;
}