Skip to content

Commit 98d442f

Browse files
committed
Computing strategies from diffs
1 parent ed74f9a commit 98d442f

File tree

5 files changed

+192
-202
lines changed

5 files changed

+192
-202
lines changed

lib/blockDiffMapping.js

Lines changed: 0 additions & 80 deletions
This file was deleted.

lib/contentBlockDiff.js

Lines changed: 0 additions & 122 deletions
This file was deleted.

lib/diffDecoratorStrategies.js

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
var Strategies = require('./strategies');
2+
var DIFF_TYPE = require('./diffType');
3+
4+
var EMPTY_STRATEGY = function () {};
5+
6+
/**
7+
* @param {Array<diff_match_patch.Diff>} diffs
8+
* @param {Boolean} forNewText True if the text in blockMap is the new text.
9+
* @param {DraftJS.BlockMap} blockMap The BlockMap of the ContentState to decorate
10+
* @return {Strategies} Three strategies that identify ranges of text for each type of diff.
11+
* Only two of them will actually be relevant (equal and insert for
12+
* new text, or equal and delete for old text).
13+
*/
14+
function diffDecoratorStrategies(diffs, forNewText, blockMap) {
15+
var absoluteRanges = diffToAbsoluteRanges(diffs, forNewText);
16+
17+
var modifiedMapping = mapRangesToBlocks(absoluteRanges.modified, blockMap);
18+
var equalMapping = mapRangesToBlocks(absoluteRanges.equal, blockMap);
19+
20+
var modifiedStrategy = strategyFromMapping(modifiedMapping, blockMap);
21+
var equalStrategy = strategyFromMapping(equalMapping, blockMap);
22+
23+
if (forNewText) {
24+
return new Strategies({
25+
delete: EMPTY_STRATEGY,
26+
equal: equalStrategy,
27+
insert: modifiedStrategy
28+
});
29+
} else {
30+
return new Strategies({
31+
delete: modifiedStrategy,
32+
equal: equalStrategy,
33+
insert: EMPTY_STRATEGY
34+
});
35+
}
36+
}
37+
38+
/**
39+
* Returns the absolute ranges for equal and modified (insert or delete) texts.
40+
* @param {Array<diff_match_patch.Diff>} diffs
41+
* @param {Boolean} forNewText
42+
* @returns {Object<Array<Range>>} Two list of ranges (equal and modified)
43+
*/
44+
function diffToAbsoluteRanges(diffs, forNewText) {
45+
var absoluteRanges = {
46+
equal: [],
47+
modified: []
48+
};
49+
var typeToIgnore = forNewText ? DIFF_TYPE.DELETE : DIFF_TYPE.INSERT;
50+
51+
var charIndex = 0;
52+
diffs.forEach(function (diff) {
53+
var diffType = diff[0];
54+
var diffText = diff[1];
55+
56+
if (diffType === typeToIgnore) {
57+
return;
58+
}
59+
60+
var range = {
61+
start: charIndex,
62+
end: charIndex + diffText.length
63+
};
64+
if (diffType === DIFF_TYPE.EQUAL) {
65+
absoluteRanges.equal.push(range);
66+
} else {
67+
absoluteRanges.modified.push(range);
68+
}
69+
// Progress in the text
70+
charIndex += diffText.length;
71+
});
72+
73+
return absoluteRanges;
74+
}
75+
76+
/**
77+
* @param {Array<Range>} absoluteRanges The ranges for the whole text
78+
* @param {DraftJS.BlockMap} blockMap The BlockMap of the ContentState to decorate
79+
* @returns {Immutable.Map<BlockKey, Array<Range>>} Ranges are relative to each block.
80+
*/
81+
function mapRangesToBlocks(absoluteRanges, blockMap) {
82+
var blockStartIndex = 0;
83+
return blockMap.map(function (block) {
84+
var ranges = findRangesBetween(absoluteRanges,
85+
blockStartIndex,
86+
blockStartIndex + block.getLength());
87+
blockStartIndex += block.getLength() + 1; // Account for possible '\n'
88+
return ranges;
89+
});
90+
}
91+
92+
/**
93+
* @param {Array<Range>} ranges
94+
* @param {Number} start
95+
* @param {Number} end
96+
* @returns {Array<Range>} All the ranges that overlapped
97+
* start-end, cropped and re-indexed to be relative to start.
98+
*/
99+
function findRangesBetween(ranges, start, end) {
100+
var res = [];
101+
ranges.forEach(function (range) {
102+
if (range.start < end && range.end > start) {
103+
// Crop the range
104+
var intersectionStart = Math.max(range.start, start);
105+
var intersectionEnd = Math.min(range.end, end);
106+
// Push relative range
107+
res.push({
108+
start: intersectionStart - start,
109+
end: intersectionEnd - start
110+
});
111+
}
112+
});
113+
return res;
114+
}
115+
116+
/**
117+
* @returns {Immutable.Map<BlockKey, Array<Range>>} mappedRanges
118+
* @returns {DraftJS.BlockMap} blockMap
119+
* @returns {DraftJS.DecoratorStrategy} A strategy applying to the
120+
* ranges provided for each block. Once the block's content change, the
121+
* block will not be decorated anymore.
122+
*/
123+
function strategyFromMapping(mappedRanges, blockMap) {
124+
// Save the original blockMap's content for later comparison
125+
return function (contentBlock, callback) {
126+
var key = contentBlock.getKey();
127+
var ranges = mappedRanges.get(key);
128+
if (!ranges) {
129+
return;
130+
}
131+
var oldContent = blockMap.get(key).getText();
132+
var newContent = contentBlock.getText();
133+
// If the content is still the same
134+
if (oldContent === newContent) {
135+
ranges.forEach(function (range) {
136+
callback(range.start, range.end);
137+
});
138+
}
139+
};
140+
}
141+
142+
module.exports = diffDecoratorStrategies;

lib/diffType.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
var diff_word_mode = require('./diff-word-mode');
2+
3+
var DIFF_TYPE = {
4+
EQUAL: diff_word_mode.DIFF_EQUAL,
5+
INSERT: diff_word_mode.DIFF_INSERT,
6+
DELETE: diff_word_mode.DIFF_DELETE
7+
};
8+
9+
modules.exports = DIFF_TYPE;

lib/strategies.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
var DIFF_TYPE = require('./diffType');
2+
3+
/**
4+
* Structure to hold the three types of strategies together
5+
*/
6+
function Strategies(strategies) {
7+
this.del = strategies.delete;
8+
this.equal = strategies.equal;
9+
this.insert = strategies.insert;
10+
}
11+
12+
/**
13+
* @param {DiffType} type
14+
* @returns {Draft.DecoratorStrategy} The strategy for the given type of diff
15+
*/
16+
Strategies.prototype.getStrategy = function (type) {
17+
switch (type) {
18+
case DIFF_TYPE.EQUAL :
19+
return this.equal;
20+
case DIFF_TYPE.INSERT :
21+
return this.insert;
22+
case DIFF_TYPE.DELETE :
23+
return this.del;
24+
default:
25+
throw new Error('Unknown diff type ' + type);
26+
}
27+
};
28+
29+
Strategies.prototype.getEqualStrategy = function () {
30+
return this.equal;
31+
};
32+
33+
Strategies.prototype.getDeleteStrategy = function () {
34+
return this.del;
35+
};
36+
37+
Strategies.prototype.getInsertStrategy = function () {
38+
return this.insert;
39+
};
40+
41+
module.exports = Strategies;

0 commit comments

Comments
 (0)