Skip to content

Commit 1955d3f

Browse files
committed
v2 & rename to universal-diff
1 parent 93ec4bf commit 1955d3f

File tree

12 files changed

+225
-80
lines changed

12 files changed

+225
-80
lines changed

README.md

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,56 @@
1-
diff-merge
1+
universal-diff
22
==========
33

44
diff & merge algorithm realized with Javascript
55

6-
There are two compare methods: simple/myers(used as default), while the latter performs better in most situations(O(ND)).
7-
86
### Usage
97

108
- nodejs
119

12-
var _ = require('diff-merge'),
10+
var _ = require('universal-diff'),
1311
compare = _.compare,
14-
merge = _.merge;
12+
merge = _.merge,
13+
compareStr = _.compareStr,
14+
mergeStr = _.mergeStr;
1515

1616
- browser
1717

18-
<script type="text/javascript" src="../dist/diff.min.js"></script>
18+
<script type="text/javascript" src="diff.min.js"></script>
1919
<script type="text/javascript">
2020
var _ = window.diff,
2121
compare = _.compare,
22-
merge = _.merge;
22+
merge = _.merge,
23+
compareStr = _.compareStr,
24+
mergeStr = _.mergeStr;
2325
</script>
2426

2527
### Compare
2628

29+
var seq1 = [1, 2, 'a', 'b'],
30+
seq2 = [1, 2, 'c', 'b'];
31+
32+
var seqResult = compare(seq1, seq2); // seqResult: [[2, 1, ['c']]
33+
2734
var s1 = 'abc',
2835
s2 = 'abcd',
2936
splitter = '';
3037

31-
var compareResult = compare(s1, s2, splitter);
38+
var strResult = compareStr(s1, s2, splitter); // strResult: { splitter: '', diff: [[3, 0, 'd']] }
3239

3340
### Merge
3441

35-
var s3 = merge(s1, compareResult);
42+
var seq3 = merge(seq1, seqResult); // seq3: [1, 2, 'b']
43+
44+
var s3 = mergeStr(s1, strResult); // s3: 'abcd'
3645

3746
### Test
3847

39-
test/test.html
48+
gulp test
4049

41-
### Algorithm
50+
### Build
51+
52+
gulp
4253

43-
SIMPLE: http://en.wikipedia.org/wiki/Levenshtein_distance
54+
### Algorithm
4455

4556
MYERS': https://neil.fraser.name/software/diff_match_patch/myers.pdf

bower.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
2-
"name": "diff-merge",
2+
"name": "universal-diff",
33
"main": "dist/index.js",
44
"version": "1.0.1",
5-
"homepage": "https://github.com/nighca/diff-merge",
5+
"homepage": "https://github.com/nighca/universal-diff",
66
"authors": [
77
"nighca <[email protected]>"
88
],

dist/diff.js

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*! diff-merge v1.0.1 | nighca([email protected]) | Apache License(2.0) */
1+
/*! universal-diff v2.0.0 | nighca([email protected]) | Apache License(2.0) */
22

33
(function(global, undefined){
44

@@ -17,15 +17,21 @@ var MARK_EMPTY = -1,
1717
MARK_SAME = 0;
1818

1919

20+
var defaultEqual = function(a, b){
21+
return a === b;
22+
};
23+
2024
// caculate min-edit-script (naive)
21-
var naiveCompare = function(seq1, seq2){
25+
var naiveCompare = function(seq1, seq2, eq){
2226

2327
var l1 = seq1.length,
2428
l2 = seq2.length,
2529
distMap = Array.apply(null, {length: l1 + 1}).map(function(){return [];}),
2630
stepMap = Array.apply(null, {length: l1 + 1}).map(function(){return [];}),
2731
i, j;
2832

33+
eq = eq || defaultEqual;
34+
2935
for(i = 0; i <= l1; i++){
3036
for(j = 0; j <= l2; j++){
3137

@@ -34,7 +40,7 @@ var naiveCompare = function(seq1, seq2){
3440
stepMap[i][j] = i > 0 ? STEP_REMOVE : STEP_INSERT;
3541

3642
}else{
37-
var equal = seq1[i-1] === seq2[j-1],
43+
var equal = eq(seq1[i-1], seq2[j-1]),
3844

3945
removeDist = distMap[i-1][j] + 1,
4046
insertDist = distMap[i][j-1] + 1,
@@ -65,7 +71,7 @@ var naiveCompare = function(seq1, seq2){
6571
};
6672

6773
// caculate min-edit-script (myers)
68-
var myersCompare = function(seq1, seq2){
74+
var myersCompare = function(seq1, seq2, eq){
6975

7076
var N = seq1.length,
7177
M = seq2.length,
@@ -74,6 +80,8 @@ var myersCompare = function(seq1, seq2){
7480
furthestReaching = [],
7581
dist = -1;
7682

83+
eq = eq || defaultEqual;
84+
7785
furthestReaching[MAX + 1] = 0;
7886

7987
// caculate min distance & log each step
@@ -90,7 +98,7 @@ var myersCompare = function(seq1, seq2){
9098
y = x - k;
9199
stepMap[x][y] = step;
92100

93-
while(x < N && y < M && seq1[x] === seq2[y]){
101+
while(x < N && y < M && eq(seq1[x], seq2[y])){
94102
x++;
95103
y++;
96104
stepMap[x][y] = STEP_NOCHANGE;
@@ -158,10 +166,10 @@ var transformStepMap = function(seq1, seq2, stepMap){
158166
};
159167

160168
// get edit script
161-
var compare = function(seq1, seq2){
169+
var compare = function(seq1, seq2, eq){
162170

163171
// do compare
164-
var stepMap = coreCompare(seq1, seq2);
172+
var stepMap = coreCompare(seq1, seq2, eq);
165173

166174
// transform stepMap
167175
var contrast = transformStepMap(seq1, seq2, stepMap),
@@ -196,8 +204,24 @@ var compare = function(seq1, seq2){
196204
return script;
197205
};
198206

207+
// merge
208+
var merge = function(seq, script){
209+
var result = seq.slice();
210+
211+
for(var i = script.length - 1, modify; i >= 0; i--){
212+
modify = script[i];
213+
var to = modify[2];
214+
if(to){
215+
modify = modify.slice(0, 2).concat(to);
216+
}
217+
result.splice.apply(result, modify);
218+
}
219+
220+
return result;
221+
};
222+
199223
// compare string (use splitter)
200-
var compareStr = function(str1, str2, splitter) {
224+
var compareStr = function(str1, str2, splitter){
201225
splitter = typeof splitter === 'string' ? splitter : '';
202226

203227
var seq1 = str1.split(splitter),
@@ -216,7 +240,7 @@ var compareStr = function(str1, str2, splitter) {
216240
};
217241
};
218242

219-
// merge string (add spliter bacj=k)
243+
// merge string (add spliter back)
220244
var mergeStr = function(cnt, compareResult){
221245
var splitter = compareResult.splitter,
222246
diff = compareResult.diff,
@@ -230,11 +254,25 @@ var mergeStr = function(cnt, compareResult){
230254
return result.join(splitter);
231255
};
232256

233-
global.diff = {
257+
var diff = {
234258
coreCompare: coreCompare,
235259
compare: compare,
260+
merge: merge,
236261
compareStr: compareStr,
237262
mergeStr: mergeStr
238263
};
239264

265+
// RequireJS && SeaJS
266+
if(typeof define === 'function'){
267+
define(function(){
268+
return diff;
269+
});
270+
271+
// NodeJS
272+
}else if(typeof exports !== 'undefined'){
273+
module.exports = diff;
274+
}else{
275+
global.diff = diff;
276+
}
277+
240278
})(this);

dist/diff.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gulpfile.js

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,31 @@
1-
var gulp = require('gulp');
2-
var concat = require('gulp-concat');
3-
var uglify = require('gulp-uglify');
4-
var replace = require('gulp-replace');
5-
var del = require('del');
1+
var gulp = require('gulp'),
2+
concat = require('gulp-concat'),
3+
uglify = require('gulp-uglify'),
4+
replace = require('gulp-replace'),
5+
del = require('del'),
6+
mocha = require('gulp-mocha');
67

7-
var info = require('./package.json');
8-
9-
var head = '/*! diff-merge v' + info.version + ' | ' + info.author.name + '(' + info.author.email + ') | Apache License(2.0) */\n';
8+
var info = require('./package.json'),
9+
head = '/*! universal-diff v' + info.version + ' | ' + info.author.name + '(' + info.author.email + ') | Apache License(2.0) */\n';
1010

1111
var paths = {
1212
dist: 'dist',
13-
scripts: ['lib/head.js', 'lib/define.js', 'lib/core.js', 'lib/script.js', 'lib/string.js', 'lib/tail.js']
13+
scripts: ['lib/head.js', 'lib/define.js', 'lib/core.js', 'lib/script.js', 'lib/string.js', 'lib/tail.js'],
14+
tests: ['test/script.js', 'test/string.js']
1415
};
1516

17+
// do test
18+
gulp.task('test', function(){
19+
return gulp.src(paths.tests, {read: false})
20+
.pipe(mocha());
21+
});
22+
23+
// clean dist folder
1624
gulp.task('clean', function(cb) {
1725
del([paths.dist], cb);
1826
});
1927

28+
// build uncompressed result
2029
gulp.task('build-dev', ['clean'], function() {
2130
return gulp.src(paths.scripts)
2231
.pipe(concat('diff.js'))
@@ -25,6 +34,7 @@ gulp.task('build-dev', ['clean'], function() {
2534
.pipe(gulp.dest(paths.dist));
2635
});
2736

37+
// build compressed result
2838
gulp.task('build', ['clean'], function() {
2939
return gulp.src(paths.scripts)
3040
.pipe(concat('diff.min.js'))

lib/core.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,21 @@
33
44
*/
55

6+
var defaultEqual = function(a, b){
7+
return a === b;
8+
};
9+
610
// caculate min-edit-script (naive)
7-
var naiveCompare = function(seq1, seq2){
11+
var naiveCompare = function(seq1, seq2, eq){
812

913
var l1 = seq1.length,
1014
l2 = seq2.length,
1115
distMap = Array.apply(null, {length: l1 + 1}).map(function(){return [];}),
1216
stepMap = Array.apply(null, {length: l1 + 1}).map(function(){return [];}),
1317
i, j;
1418

19+
eq = eq || defaultEqual;
20+
1521
for(i = 0; i <= l1; i++){
1622
for(j = 0; j <= l2; j++){
1723

@@ -20,7 +26,7 @@ var naiveCompare = function(seq1, seq2){
2026
stepMap[i][j] = i > 0 ? STEP_REMOVE : STEP_INSERT;
2127

2228
}else{
23-
var equal = seq1[i-1] === seq2[j-1],
29+
var equal = eq(seq1[i-1], seq2[j-1]),
2430

2531
removeDist = distMap[i-1][j] + 1,
2632
insertDist = distMap[i][j-1] + 1,
@@ -51,7 +57,7 @@ var naiveCompare = function(seq1, seq2){
5157
};
5258

5359
// caculate min-edit-script (myers)
54-
var myersCompare = function(seq1, seq2){
60+
var myersCompare = function(seq1, seq2, eq){
5561

5662
var N = seq1.length,
5763
M = seq2.length,
@@ -60,6 +66,8 @@ var myersCompare = function(seq1, seq2){
6066
furthestReaching = [],
6167
dist = -1;
6268

69+
eq = eq || defaultEqual;
70+
6371
furthestReaching[MAX + 1] = 0;
6472

6573
// caculate min distance & log each step
@@ -76,7 +84,7 @@ var myersCompare = function(seq1, seq2){
7684
y = x - k;
7785
stepMap[x][y] = step;
7886

79-
while(x < N && y < M && seq1[x] === seq2[y]){
87+
while(x < N && y < M && eq(seq1[x], seq2[y])){
8088
x++;
8189
y++;
8290
stepMap[x][y] = STEP_NOCHANGE;

lib/script.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ var transformStepMap = function(seq1, seq2, stepMap){
5050
};
5151

5252
// get edit script
53-
var compare = function(seq1, seq2){
53+
var compare = function(seq1, seq2, eq){
5454

5555
// do compare
56-
var stepMap = coreCompare(seq1, seq2);
56+
var stepMap = coreCompare(seq1, seq2, eq);
5757

5858
// transform stepMap
5959
var contrast = transformStepMap(seq1, seq2, stepMap),
@@ -94,9 +94,9 @@ var merge = function(seq, script){
9494

9595
for(var i = script.length - 1, modify; i >= 0; i--){
9696
modify = script[i];
97-
var to = modify[3];
97+
var to = modify[2];
9898
if(to){
99-
modify =
99+
modify = modify.slice(0, 2).concat(to);
100100
}
101101
result.splice.apply(result, modify);
102102
}

lib/string.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*/
44

55
// compare string (use splitter)
6-
var compareStr = function(str1, str2, splitter) {
6+
var compareStr = function(str1, str2, splitter){
77
splitter = typeof splitter === 'string' ? splitter : '';
88

99
var seq1 = str1.split(splitter),

lib/tail.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,25 @@
33
44
*/
55

6-
global.diff = {
6+
var diff = {
77
coreCompare: coreCompare,
88
compare: compare,
9+
merge: merge,
910
compareStr: compareStr,
1011
mergeStr: mergeStr
1112
};
1213

14+
// RequireJS && SeaJS
15+
if(typeof define === 'function'){
16+
define(function(){
17+
return diff;
18+
});
19+
20+
// NodeJS
21+
}else if(typeof exports !== 'undefined'){
22+
module.exports = diff;
23+
}else{
24+
global.diff = diff;
25+
}
26+
1327
})(this);

0 commit comments

Comments
 (0)