Skip to content

Commit a5cecd8

Browse files
committed
dont split surrogate pairs, fixes #1
1 parent de7329a commit a5cecd8

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed

β€Ždiff.jsβ€Ž

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ function diff_main(text1, text2, cursor_pos) {
8282
if (cursor_pos != null) {
8383
diffs = fix_cursor(diffs, cursor_pos);
8484
}
85+
diffs = fix_emoji(diffs);
8586
return diffs;
8687
};
8788

@@ -671,7 +672,46 @@ function fix_cursor (diffs, cursor_pos) {
671672
return diffs;
672673
}
673674
}
675+
}
676+
677+
/*
678+
* Check diff did not split surrogate pairs.
679+
* Ex. [0, '\uD83D'], [-1, '\uDC36'], [1, '\uDC2F'] -> [-1, '\uD83D\uDC36'], [1, '\uD83D\uDC2F']
680+
* '\uD83D\uDC36' === '🐢', '\uD83D\uDC2F' === '🐯'
681+
*
682+
* @param {Array} diffs Array of diff tuples
683+
* @return {Array} Array of diff tuples
684+
*/
685+
function fix_emoji (diffs) {
686+
var compact = false;
687+
var starts_with_pair_end = function(str) {
688+
return str.charCodeAt(0) >= 0xDC00 && str.charCodeAt(0) <= 0xDFFF;
689+
}
690+
var ends_with_pair_start = function(str) {
691+
return str.charCodeAt(str.length-1) >= 0xD800 && str.charCodeAt(str.length-1) <= 0xDBFF;
692+
}
693+
for (var i = 2; i < diffs.length; i += 1) {
694+
if (diffs[i-2][0] === DIFF_EQUAL && ends_with_pair_start(diffs[i-2][1]) &&
695+
diffs[i-1][0] === DIFF_DELETE && starts_with_pair_end(diffs[i-1][1]) &&
696+
diffs[i][0] === DIFF_INSERT && starts_with_pair_end(diffs[i][1])) {
697+
compact = true;
698+
699+
diffs[i-1][1] = diffs[i-2][1].slice(-1) + diffs[i-1][1];
700+
diffs[i][1] = diffs[i-2][1].slice(-1) + diffs[i][1];
674701

702+
diffs[i-2][1] = diffs[i-2][1].slice(0, -1);
703+
}
704+
}
705+
if (!compact) {
706+
return diffs;
707+
}
708+
var fixed_diffs = [];
709+
for (var i = 0; i < diffs.length; i += 1) {
710+
if (diffs[i][1].length > 0) {
711+
fixed_diffs.push(diffs[i]);
712+
}
713+
}
714+
return fixed_diffs;
675715
}
676716

677717
/*

β€Žtest.jsβ€Ž

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,43 @@ for(var i = 0; i < ITERATIONS; ++i) {
4949
}
5050
}
5151

52+
console.log('Running emoji tests');
53+
(function() {
54+
var result = diff('🐢', '🐯');
55+
var expected = [
56+
[diff.DELETE, '🐢'],
57+
[diff.INSERT, '🐯'],
58+
];
59+
if (!_.isEqual(result, expected)) {
60+
console.log(result, '!==', expected);
61+
throw new Error('Emoji simple case test failed');
62+
}
63+
})();
64+
65+
(function() {
66+
var result = diff('πŸ‘¨πŸ½', 'πŸ‘©πŸ½');
67+
var expected = [
68+
[diff.DELETE, 'πŸ‘¨'],
69+
[diff.INSERT, 'πŸ‘©'],
70+
[diff.EQUAL, '🏽']
71+
];
72+
if (!_.isEqual(result, expected)) {
73+
console.log(result, '!==', expected);
74+
throw new Error('Emoji before case test failed');
75+
}
76+
})();
77+
78+
(function() {
79+
var result = diff('πŸ‘©πŸΌ', 'πŸ‘©πŸ½');
80+
var expected = [
81+
[diff.EQUAL, 'πŸ‘©'],
82+
[diff.DELETE, '🏼'],
83+
[diff.INSERT, '🏽'],
84+
];
85+
if (!_.isEqual(result, expected)) {
86+
console.log(result, '!==', expected);
87+
throw new Error('Emoji after case test failed');
88+
}
89+
})();
90+
5291
console.log("Success!");

0 commit comments

Comments
Β (0)