Skip to content

Commit 93ec4bf

Browse files
committed
init v2
1 parent abdaab5 commit 93ec4bf

File tree

9 files changed

+396
-66
lines changed

9 files changed

+396
-66
lines changed

dist/diff.js

Lines changed: 132 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -3,43 +3,74 @@
33
(function(global, undefined){
44

55

6-
var compare = function(cnt1, cnt2, splitter){
7-
var SPLITTER = typeof splitter === 'string' ? splitter : '',
8-
9-
MARK_EMPTY = -1,
10-
MARK_SAME = 0,
11-
12-
STEP_NOCHANGE = 0,
13-
STEP_REPLACE = 1,
14-
STEP_REMOVE = 2,
15-
STEP_INSERT = 3;
16-
17-
// result object
18-
var diff = [],
19-
result = {
20-
splitter: SPLITTER,
21-
diff: diff
22-
};
23-
24-
// if string equal
25-
if(cnt1 === cnt2){
26-
return result;
27-
}
6+
// steps
7+
8+
var STEP_NOCHANGE = 0,
9+
STEP_REPLACE = 1,
10+
STEP_REMOVE = 2,
11+
STEP_INSERT = 3;
12+
13+
14+
// script marks
15+
16+
var MARK_EMPTY = -1,
17+
MARK_SAME = 0;
18+
19+
20+
// caculate min-edit-script (naive)
21+
var naiveCompare = function(seq1, seq2){
22+
23+
var l1 = seq1.length,
24+
l2 = seq2.length,
25+
distMap = Array.apply(null, {length: l1 + 1}).map(function(){return [];}),
26+
stepMap = Array.apply(null, {length: l1 + 1}).map(function(){return [];}),
27+
i, j;
2828

29-
// convert string to array
30-
var arr1, arr2;
31-
if(typeof splitter === 'function'){
32-
arr1 = splitter(cnt1);
33-
arr2 = splitter(cnt2);
34-
}else{
35-
arr1 = cnt1.split(SPLITTER);
36-
arr2 = cnt2.split(SPLITTER);
29+
for(i = 0; i <= l1; i++){
30+
for(j = 0; j <= l2; j++){
31+
32+
if(i === 0 || j === 0){
33+
distMap[i][j] = i || j;
34+
stepMap[i][j] = i > 0 ? STEP_REMOVE : STEP_INSERT;
35+
36+
}else{
37+
var equal = seq1[i-1] === seq2[j-1],
38+
39+
removeDist = distMap[i-1][j] + 1,
40+
insertDist = distMap[i][j-1] + 1,
41+
replaceDist = distMap[i-1][j-1] + (equal ? 0 : 2),
42+
dist = Math.min(replaceDist, removeDist, insertDist);
43+
44+
distMap[i][j] = dist;
45+
46+
switch(dist){
47+
48+
case replaceDist:
49+
stepMap[i][j] = equal ? STEP_NOCHANGE : STEP_REPLACE;
50+
break;
51+
52+
case removeDist:
53+
stepMap[i][j] = STEP_REMOVE;
54+
break;
55+
56+
case insertDist:
57+
stepMap[i][j] = STEP_INSERT;
58+
59+
}
60+
}
61+
}
3762
}
3863

39-
var N = arr1.length,
40-
M = arr2.length,
64+
return stepMap;
65+
};
66+
67+
// caculate min-edit-script (myers)
68+
var myersCompare = function(seq1, seq2){
69+
70+
var N = seq1.length,
71+
M = seq2.length,
4172
MAX = N + M,
42-
steps = Array.apply(null, {length: M+N+1}).map(function(){return [];}),
73+
stepMap = Array.apply(null, {length: M+N+1}).map(function(){return [];}),
4374
furthestReaching = [],
4475
dist = -1;
4576

@@ -57,12 +88,12 @@ var compare = function(cnt1, cnt2, splitter){
5788
}
5889

5990
y = x - k;
60-
steps[x][y] = step;
91+
stepMap[x][y] = step;
6192

62-
while(x < N && y < M && arr1[x] === arr2[y]){
93+
while(x < N && y < M && seq1[x] === seq2[y]){
6394
x++;
6495
y++;
65-
steps[x][y] = STEP_NOCHANGE;
96+
stepMap[x][y] = STEP_NOCHANGE;
6697
}
6798

6899
furthestReaching[k + MAX] = x;
@@ -73,47 +104,75 @@ var compare = function(cnt1, cnt2, splitter){
73104
}
74105
}
75106

107+
return stepMap;
108+
};
109+
110+
// use myers as default
111+
var coreCompare = myersCompare;
112+
113+
// stepMap to contrast array
114+
var transformStepMap = function(seq1, seq2, stepMap){
76115
// get contrast arrays (src & target) by analyze step by step
77-
var src = [], target = [];
116+
var l1 = seq1.length,
117+
l2 = seq2.length,
118+
src = [], target = [];
78119

79-
for(var i = N,j = M; i > 0 || j > 0;){
80-
switch(steps[i][j]){
120+
for(var i = l1,j = l2; i > 0 || j > 0;){
121+
switch(stepMap[i][j]){
81122

82123
case STEP_NOCHANGE:
83-
src.unshift(arr1[i-1]);
124+
src.unshift(seq1[i-1]);
84125
target.unshift(MARK_SAME);
85126
i -= 1;
86127
j -= 1;
87128
break;
88129

89130
case STEP_REPLACE:
90-
src.unshift(arr1[i-1]);
91-
target.unshift(arr2[j-1]);
131+
src.unshift(seq1[i-1]);
132+
target.unshift(seq2[j-1]);
92133
i -= 1;
93134
j -= 1;
94135
break;
95136

96137
case STEP_REMOVE:
97-
src.unshift(arr1[i-1]);
138+
src.unshift(seq1[i-1]);
98139
target.unshift(MARK_EMPTY);
99140
i -= 1;
100141
j -= 0;
101142
break;
102143

103144
case STEP_INSERT:
104145
src.unshift(MARK_EMPTY);
105-
target.unshift(arr2[j-1]);
146+
target.unshift(seq2[j-1]);
106147
i -= 0;
107148
j -= 1;
108149
break;
109150

110151
}
111152
}
112153

113-
// convert contrast arrays to diff array
154+
return {
155+
src: src,
156+
target: target
157+
};
158+
};
159+
160+
// get edit script
161+
var compare = function(seq1, seq2){
162+
163+
// do compare
164+
var stepMap = coreCompare(seq1, seq2);
165+
166+
// transform stepMap
167+
var contrast = transformStepMap(seq1, seq2, stepMap),
168+
src = contrast.src,
169+
target = contrast.target;
170+
171+
// convert contrast arrays to edit script
114172
var l = target.length,
115173
start, len, to,
116-
notEmpty = function(s){return s !== MARK_EMPTY;};
174+
notEmpty = function(s){return s !== MARK_EMPTY;},
175+
script = [];
117176

118177
for(i = l - 1; i >= 0;){
119178
// join continuous diffs
@@ -124,24 +183,41 @@ var compare = function(cnt1, cnt2, splitter){
124183
len = src.slice(j + 1, i + 1).filter(notEmpty).length; // length should be replaced (on src)
125184
to = target.slice(j + 1, i + 1).filter(notEmpty); // new content
126185

127-
diff.unshift(
186+
script.unshift(
128187
to.length ?
129-
[start, len, to.join(SPLITTER)] : // replace
188+
[start, len, to] : // replace
130189
[start, len] // remove
131190
);
132191
}
133192

134193
i = j - 1;
135194
}
136195

137-
return result;
196+
return script;
138197
};
139198

140-
if(typeof module === "object" && typeof module.exports === "object"){
141-
module.exports = compare;
142-
}
199+
// compare string (use splitter)
200+
var compareStr = function(str1, str2, splitter) {
201+
splitter = typeof splitter === 'string' ? splitter : '';
202+
203+
var seq1 = str1.split(splitter),
204+
seq2 = str2.split(splitter),
205+
script = compare(seq1, seq2);
206+
207+
script.forEach(function(change){
208+
if(change[2]){
209+
change[2] = change[2].join(splitter);
210+
}
211+
});
143212

144-
var merge = function(cnt, compareResult){
213+
return {
214+
splitter: splitter,
215+
diff: script
216+
};
217+
};
218+
219+
// merge string (add spliter bacj=k)
220+
var mergeStr = function(cnt, compareResult){
145221
var splitter = compareResult.splitter,
146222
diff = compareResult.diff,
147223
result = cnt.split(splitter);
@@ -154,13 +230,11 @@ var merge = function(cnt, compareResult){
154230
return result.join(splitter);
155231
};
156232

157-
if(typeof module === "object" && typeof module.exports === "object"){
158-
module.exports = merge;
159-
}
160-
161233
global.diff = {
234+
coreCompare: coreCompare,
162235
compare: compare,
163-
merge: merge
236+
compareStr: compareStr,
237+
mergeStr: mergeStr
164238
};
165239

166240
})(this);

dist/diff.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gulpfile.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ var head = '/*! diff-merge v' + info.version + ' | ' + info.author.name + '(' +
1010

1111
var paths = {
1212
dist: 'dist',
13-
scripts: ['lib/head.js', 'lib/myers-compare.js', 'lib/merge.js', 'lib/tail.js']
13+
scripts: ['lib/head.js', 'lib/define.js', 'lib/core.js', 'lib/script.js', 'lib/string.js', 'lib/tail.js']
1414
};
1515

1616
gulp.task('clean', function(cb) {

lib/core.js

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* core compare method
3+
4+
*/
5+
6+
// caculate min-edit-script (naive)
7+
var naiveCompare = function(seq1, seq2){
8+
9+
var l1 = seq1.length,
10+
l2 = seq2.length,
11+
distMap = Array.apply(null, {length: l1 + 1}).map(function(){return [];}),
12+
stepMap = Array.apply(null, {length: l1 + 1}).map(function(){return [];}),
13+
i, j;
14+
15+
for(i = 0; i <= l1; i++){
16+
for(j = 0; j <= l2; j++){
17+
18+
if(i === 0 || j === 0){
19+
distMap[i][j] = i || j;
20+
stepMap[i][j] = i > 0 ? STEP_REMOVE : STEP_INSERT;
21+
22+
}else{
23+
var equal = seq1[i-1] === seq2[j-1],
24+
25+
removeDist = distMap[i-1][j] + 1,
26+
insertDist = distMap[i][j-1] + 1,
27+
replaceDist = distMap[i-1][j-1] + (equal ? 0 : 2),
28+
dist = Math.min(replaceDist, removeDist, insertDist);
29+
30+
distMap[i][j] = dist;
31+
32+
switch(dist){
33+
34+
case replaceDist:
35+
stepMap[i][j] = equal ? STEP_NOCHANGE : STEP_REPLACE;
36+
break;
37+
38+
case removeDist:
39+
stepMap[i][j] = STEP_REMOVE;
40+
break;
41+
42+
case insertDist:
43+
stepMap[i][j] = STEP_INSERT;
44+
45+
}
46+
}
47+
}
48+
}
49+
50+
return stepMap;
51+
};
52+
53+
// caculate min-edit-script (myers)
54+
var myersCompare = function(seq1, seq2){
55+
56+
var N = seq1.length,
57+
M = seq2.length,
58+
MAX = N + M,
59+
stepMap = Array.apply(null, {length: M+N+1}).map(function(){return [];}),
60+
furthestReaching = [],
61+
dist = -1;
62+
63+
furthestReaching[MAX + 1] = 0;
64+
65+
// caculate min distance & log each step
66+
for(var D = 0; D <= MAX && dist === -1; D++){
67+
for(var k = -D, x, y, step; k <= D && dist === -1; k+=2){
68+
if(k === -D || (k !== D && furthestReaching[k - 1 + MAX] < furthestReaching[k + 1 + MAX])){
69+
x = furthestReaching[k + 1 + MAX];
70+
step = STEP_INSERT;
71+
}else{
72+
x = furthestReaching[k - 1 + MAX] + 1;
73+
step = STEP_REMOVE;
74+
}
75+
76+
y = x - k;
77+
stepMap[x][y] = step;
78+
79+
while(x < N && y < M && seq1[x] === seq2[y]){
80+
x++;
81+
y++;
82+
stepMap[x][y] = STEP_NOCHANGE;
83+
}
84+
85+
furthestReaching[k + MAX] = x;
86+
87+
if(x >= N && y >= M){
88+
dist = D;
89+
}
90+
}
91+
}
92+
93+
return stepMap;
94+
};
95+
96+
// use myers as default
97+
var coreCompare = myersCompare;

0 commit comments

Comments
 (0)