-
Notifications
You must be signed in to change notification settings - Fork 30
Expand file tree
/
Copy pathdiff.js
More file actions
117 lines (101 loc) · 3.17 KB
/
diff.js
File metadata and controls
117 lines (101 loc) · 3.17 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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
'use strict';
var Immutable = require('immutable');
var utils = require('./utils');
var lcs = require('./lcs');
var path = require('./path');
var concatPath = path.concat,
escape = path.escape,
op = utils.op,
isMap = utils.isMap,
isIndexed = utils.isIndexed;
var mapDiff = function(a, b, p){
var ops = [];
var path = p || '';
if(Immutable.is(a, b) || (a == b == null)){ return ops; }
var areLists = isIndexed(a) && isIndexed(b);
var lastKey = null;
var removeKey = null
if(a.forEach){
a.forEach(function(aValue, aKey){
if(b.has(aKey)){
if(isMap(aValue) && isMap(b.get(aKey))){
ops = ops.concat(mapDiff(aValue, b.get(aKey), concatPath(path, escape(aKey))));
}
else if(isIndexed(b.get(aKey)) && isIndexed(aValue)){
ops = ops.concat(sequenceDiff(aValue, b.get(aKey), concatPath(path, escape(aKey))));
}
else {
var bValue = b.get ? b.get(aKey) : b;
var areDifferentValues = (aValue !== bValue);
if (areDifferentValues) {
ops.push(op('replace', concatPath(path, escape(aKey)), bValue));
}
}
}
else {
if(areLists){
removeKey = (lastKey != null && (lastKey+1) === aKey) ? removeKey : aKey;
ops.push( op('remove', concatPath(path, escape(removeKey))) );
lastKey = aKey;
}
else{
ops.push( op('remove', concatPath(path, escape(aKey))) );
}
}
});
}
b.forEach(function(bValue, bKey){
if(a.has && !a.has(bKey)){
ops.push( op('add', concatPath(path, escape(bKey)), bValue) );
}
});
return ops;
};
var sequenceDiff = function (a, b, p) {
var ops = [];
var path = p || '';
if(Immutable.is(a, b) || (a == b == null)){ return ops; }
if((a.count() + 1) * (b.count() + 1) >= 10000 ) { return mapDiff(a, b, p); }
var lcsDiff = lcs.diff(a, b);
var pathIndex = 0;
lcsDiff.forEach(function (diff) {
if(diff.op === '='){ pathIndex++; }
else if(diff.op === '!='){
if(isMap(diff.val) && isMap(diff.newVal)){
var mapDiffs = mapDiff(diff.val, diff.newVal, concatPath(path, pathIndex));
ops = ops.concat(mapDiffs);
}
else{
ops.push(op('replace', concatPath(path, pathIndex), diff.newVal));
}
pathIndex++;
}
else if(diff.op === '+'){
ops.push(op('add', concatPath(path, pathIndex), diff.val));
pathIndex++;
}
else if(diff.op === '-'){ ops.push(op('remove', concatPath(path, pathIndex))); }
});
return ops;
};
var primitiveTypeDiff = function (a, b, p) {
var path = p || '';
if(a === b){ return []; }
else{
return [ op('replace', concatPath(path, ''), b) ];
}
};
var diff = function(a, b, p){
if(Immutable.is(a, b)){ return Immutable.List(); }
if(a != b && (a == null || b == null)){ return Immutable.fromJS([op('replace', '/', b)]); }
if(isIndexed(a) && isIndexed(b)){
return Immutable.fromJS(sequenceDiff(a, b));
}
else if(isMap(a) && isMap(b)){
return Immutable.fromJS(mapDiff(a, b));
}
else{
return Immutable.fromJS(primitiveTypeDiff(a, b, p));
}
};
module.exports = diff;