@limetech/lime-elements 39.9.0 → 39.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/cjs/{component-DC8AD-ZG.js → component-BrNWuGdj.js} +1 -1
- package/dist/cjs/{component-BflTavfP.js → component-CFk7Dfoi.js} +3 -3
- package/dist/cjs/{component--XvTU1MI.js → component-y8swlNNR.js} +1 -1
- package/dist/cjs/lime-elements.cjs.js +1 -1
- package/dist/cjs/limel-breadcrumbs_7.cjs.entry.js +4 -4
- package/dist/cjs/limel-button-group.cjs.entry.js +3 -7
- package/dist/cjs/limel-chip_2.cjs.entry.js +4 -4
- package/dist/cjs/limel-code-diff.cjs.entry.js +645 -711
- package/dist/cjs/limel-date-picker.cjs.entry.js +4 -4
- package/dist/cjs/limel-dialog.cjs.entry.js +2 -2
- package/dist/cjs/limel-portal_3.cjs.entry.js +3 -3
- package/dist/cjs/limel-prosemirror-adapter.cjs.entry.js +1 -1
- package/dist/cjs/limel-select.cjs.entry.js +2 -2
- package/dist/cjs/limel-slider.cjs.entry.js +2 -2
- package/dist/cjs/limel-switch.cjs.entry.js +18 -632
- package/dist/cjs/limel-tab-bar.cjs.entry.js +4 -4
- package/dist/cjs/limel-tab-panel.cjs.entry.js +1 -1
- package/dist/cjs/limel-table.cjs.entry.js +3 -3
- package/dist/cjs/limel-text-editor-link-menu.cjs.entry.js +3 -3
- package/dist/cjs/limel-text-editor.cjs.entry.js +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/{ponyfill-CNdYytGF.js → ponyfill-BgIFyUG5.js} +0 -1
- package/dist/collection/components/button-group/button-group.css +38 -33
- package/dist/collection/components/button-group/button-group.js +3 -7
- package/dist/collection/components/switch/switch.css +69 -769
- package/dist/collection/components/switch/switch.js +18 -50
- package/dist/collection/components/tab-bar/tab-bar.js +2 -2
- package/dist/collection/components/tab-panel/tab-panel.js +1 -1
- package/dist/collection/components/table/table.js +3 -3
- package/dist/collection/components/text-editor/link-menu/editor-link-menu.js +3 -3
- package/dist/collection/components/text-editor/prosemirror-adapter/prosemirror-adapter.js +1 -1
- package/dist/collection/components/text-editor/text-editor.js +1 -1
- package/dist/collection/components/tooltip/tooltip-content.js +1 -1
- package/dist/collection/components/tooltip/tooltip.js +2 -2
- package/dist/esm/{component-CfpvJ0ln.js → component-7QB0OUSF.js} +1 -1
- package/dist/esm/{component-zxri0Bi3.js → component-D4qgwaTf.js} +1 -1
- package/dist/esm/{component-BsWKksqY.js → component-wGVqvUmL.js} +3 -3
- package/dist/esm/lime-elements.js +1 -1
- package/dist/esm/limel-breadcrumbs_7.entry.js +4 -4
- package/dist/esm/limel-button-group.entry.js +4 -8
- package/dist/esm/limel-chip_2.entry.js +4 -4
- package/dist/esm/limel-code-diff.entry.js +645 -711
- package/dist/esm/limel-date-picker.entry.js +4 -4
- package/dist/esm/limel-dialog.entry.js +2 -2
- package/dist/esm/limel-portal_3.entry.js +3 -3
- package/dist/esm/limel-prosemirror-adapter.entry.js +1 -1
- package/dist/esm/limel-select.entry.js +2 -2
- package/dist/esm/limel-slider.entry.js +2 -2
- package/dist/esm/limel-switch.entry.js +19 -633
- package/dist/esm/limel-tab-bar.entry.js +4 -4
- package/dist/esm/limel-tab-panel.entry.js +1 -1
- package/dist/esm/limel-table.entry.js +3 -3
- package/dist/esm/limel-text-editor-link-menu.entry.js +3 -3
- package/dist/esm/limel-text-editor.entry.js +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm/{ponyfill-D-I_3IxW.js → ponyfill-ChRGk668.js} +1 -1
- package/dist/lime-elements/lime-elements.esm.js +1 -1
- package/dist/lime-elements/{p-89dfbd4a.entry.js → p-13f4d39d.entry.js} +1 -1
- package/dist/lime-elements/{p-68ffd790.entry.js → p-1a3a7374.entry.js} +1 -1
- package/dist/lime-elements/p-266c228c.entry.js +1 -0
- package/dist/lime-elements/{p-90f8d2ef.entry.js → p-288aa326.entry.js} +1 -1
- package/dist/lime-elements/{p-6b05db4a.entry.js → p-2af214de.entry.js} +1 -1
- package/dist/lime-elements/{p-c6e9af7c.entry.js → p-37b41bad.entry.js} +1 -1
- package/dist/lime-elements/{p-ce22f3da.entry.js → p-74cd80a9.entry.js} +4 -4
- package/dist/lime-elements/{p-70e2e60c.entry.js → p-767935ac.entry.js} +1 -1
- package/dist/lime-elements/{p-b95a42ea.entry.js → p-86eebe44.entry.js} +1 -1
- package/dist/lime-elements/{p-D7ojrUFG.js → p-BN1-aIOw.js} +1 -1
- package/dist/lime-elements/{p-ClrXWM0F.js → p-C9yTLqR8.js} +1 -1
- package/dist/lime-elements/{p-D-I_3IxW.js → p-ChRGk668.js} +3 -3
- package/dist/lime-elements/{p-BDAjvVhN.js → p-DZkKQUDM.js} +3 -3
- package/dist/lime-elements/{p-dcf3cc71.entry.js → p-b2ea92be.entry.js} +1 -1
- package/dist/lime-elements/p-b3622713.entry.js +1 -0
- package/dist/lime-elements/{p-8ec4fdee.entry.js → p-cd722648.entry.js} +2 -2
- package/dist/lime-elements/{p-ee3afb60.entry.js → p-cdd5b814.entry.js} +9 -9
- package/dist/lime-elements/{p-8ec92025.entry.js → p-db51323c.entry.js} +1 -1
- package/dist/lime-elements/{p-64f664b0.entry.js → p-e2f1b070.entry.js} +1 -1
- package/dist/lime-elements/p-f10ca306.entry.js +1 -0
- package/dist/types/components/switch/switch.d.ts +4 -6
- package/package.json +3 -3
- package/dist/lime-elements/p-5be668d8.entry.js +0 -1
- package/dist/lime-elements/p-644911cc.entry.js +0 -68
- package/dist/lime-elements/p-965288d2.entry.js +0 -1
|
@@ -3,390 +3,402 @@
|
|
|
3
3
|
var index = require('./index-BjHIBY-I.js');
|
|
4
4
|
var translations = require('./translations-Bu_0fli7.js');
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
6
|
+
class Diff {
|
|
7
|
+
diff(oldStr, newStr,
|
|
8
|
+
// Type below is not accurate/complete - see above for full possibilities - but it compiles
|
|
9
|
+
options = {}) {
|
|
10
|
+
let callback;
|
|
11
|
+
if (typeof options === 'function') {
|
|
12
|
+
callback = options;
|
|
13
|
+
options = {};
|
|
14
|
+
}
|
|
15
|
+
else if ('callback' in options) {
|
|
16
|
+
callback = options.callback;
|
|
17
|
+
}
|
|
18
|
+
// Allow subclasses to massage the input prior to running
|
|
19
|
+
const oldString = this.castInput(oldStr, options);
|
|
20
|
+
const newString = this.castInput(newStr, options);
|
|
21
|
+
const oldTokens = this.removeEmpty(this.tokenize(oldString, options));
|
|
22
|
+
const newTokens = this.removeEmpty(this.tokenize(newString, options));
|
|
23
|
+
return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
|
|
24
|
+
}
|
|
25
|
+
diffWithOptionsObj(oldTokens, newTokens, options, callback) {
|
|
26
|
+
var _a;
|
|
27
|
+
const done = (value) => {
|
|
28
|
+
value = this.postProcess(value, options);
|
|
29
|
+
if (callback) {
|
|
30
|
+
setTimeout(function () { callback(value); }, 0);
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const newLen = newTokens.length, oldLen = oldTokens.length;
|
|
38
|
+
let editLength = 1;
|
|
39
|
+
let maxEditLength = newLen + oldLen;
|
|
40
|
+
if (options.maxEditLength != null) {
|
|
41
|
+
maxEditLength = Math.min(maxEditLength, options.maxEditLength);
|
|
42
|
+
}
|
|
43
|
+
const maxExecutionTime = (_a = options.timeout) !== null && _a !== void 0 ? _a : Infinity;
|
|
44
|
+
const abortAfterTimestamp = Date.now() + maxExecutionTime;
|
|
45
|
+
const bestPath = [{ oldPos: -1, lastComponent: undefined }];
|
|
46
|
+
// Seed editLength = 0, i.e. the content starts with the same values
|
|
47
|
+
let newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
|
|
48
|
+
if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
|
|
49
|
+
// Identity per the equality and tokenizer
|
|
50
|
+
return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
|
|
51
|
+
}
|
|
52
|
+
// Once we hit the right edge of the edit graph on some diagonal k, we can
|
|
53
|
+
// definitely reach the end of the edit graph in no more than k edits, so
|
|
54
|
+
// there's no point in considering any moves to diagonal k+1 any more (from
|
|
55
|
+
// which we're guaranteed to need at least k+1 more edits).
|
|
56
|
+
// Similarly, once we've reached the bottom of the edit graph, there's no
|
|
57
|
+
// point considering moves to lower diagonals.
|
|
58
|
+
// We record this fact by setting minDiagonalToConsider and
|
|
59
|
+
// maxDiagonalToConsider to some finite value once we've hit the edge of
|
|
60
|
+
// the edit graph.
|
|
61
|
+
// This optimization is not faithful to the original algorithm presented in
|
|
62
|
+
// Myers's paper, which instead pointlessly extends D-paths off the end of
|
|
63
|
+
// the edit graph - see page 7 of Myers's paper which notes this point
|
|
64
|
+
// explicitly and illustrates it with a diagram. This has major performance
|
|
65
|
+
// implications for some common scenarios. For instance, to compute a diff
|
|
66
|
+
// where the new text simply appends d characters on the end of the
|
|
67
|
+
// original text of length n, the true Myers algorithm will take O(n+d^2)
|
|
68
|
+
// time while this optimization needs only O(n+d) time.
|
|
69
|
+
let minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
|
|
70
|
+
// Main worker method. checks all permutations of a given edit length for acceptance.
|
|
71
|
+
const execEditLength = () => {
|
|
72
|
+
for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
|
|
73
|
+
let basePath;
|
|
74
|
+
const removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
|
|
75
|
+
if (removePath) {
|
|
76
|
+
// No one else is going to attempt to use this value, clear it
|
|
77
|
+
// @ts-expect-error - perf optimisation. This type-violating value will never be read.
|
|
78
|
+
bestPath[diagonalPath - 1] = undefined;
|
|
79
|
+
}
|
|
80
|
+
let canAdd = false;
|
|
81
|
+
if (addPath) {
|
|
82
|
+
// what newPos will be after we do an insertion:
|
|
83
|
+
const addPathNewPos = addPath.oldPos - diagonalPath;
|
|
84
|
+
canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
|
|
85
|
+
}
|
|
86
|
+
const canRemove = removePath && removePath.oldPos + 1 < oldLen;
|
|
87
|
+
if (!canAdd && !canRemove) {
|
|
88
|
+
// If this path is a terminal then prune
|
|
89
|
+
// @ts-expect-error - perf optimisation. This type-violating value will never be read.
|
|
90
|
+
bestPath[diagonalPath] = undefined;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
// Select the diagonal that we want to branch from. We select the prior
|
|
94
|
+
// path whose position in the old string is the farthest from the origin
|
|
95
|
+
// and does not pass the bounds of the diff graph
|
|
96
|
+
if (!canRemove || (canAdd && removePath.oldPos < addPath.oldPos)) {
|
|
97
|
+
basePath = this.addToPath(addPath, true, false, 0, options);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
basePath = this.addToPath(removePath, false, true, 1, options);
|
|
101
|
+
}
|
|
102
|
+
newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
|
|
103
|
+
if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
|
|
104
|
+
// If we have hit the end of both strings, then we are done
|
|
105
|
+
return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
bestPath[diagonalPath] = basePath;
|
|
109
|
+
if (basePath.oldPos + 1 >= oldLen) {
|
|
110
|
+
maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
|
|
111
|
+
}
|
|
112
|
+
if (newPos + 1 >= newLen) {
|
|
113
|
+
minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
editLength++;
|
|
118
|
+
};
|
|
119
|
+
// Performs the length of edit iteration. Is a bit fugly as this has to support the
|
|
120
|
+
// sync and async mode which is never fun. Loops over execEditLength until a value
|
|
121
|
+
// is produced, or until the edit length exceeds options.maxEditLength (if given),
|
|
122
|
+
// in which case it will return undefined.
|
|
123
|
+
if (callback) {
|
|
124
|
+
(function exec() {
|
|
125
|
+
setTimeout(function () {
|
|
126
|
+
if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
|
|
127
|
+
return callback(undefined);
|
|
128
|
+
}
|
|
129
|
+
if (!execEditLength()) {
|
|
130
|
+
exec();
|
|
131
|
+
}
|
|
132
|
+
}, 0);
|
|
133
|
+
}());
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
|
|
137
|
+
const ret = execEditLength();
|
|
138
|
+
if (ret) {
|
|
139
|
+
return ret;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
27
143
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
var abortAfterTimestamp = Date.now() + maxExecutionTime;
|
|
43
|
-
var bestPath = [{
|
|
44
|
-
oldPos: -1,
|
|
45
|
-
lastComponent: undefined
|
|
46
|
-
}];
|
|
47
|
-
|
|
48
|
-
// Seed editLength = 0, i.e. the content starts with the same values
|
|
49
|
-
var newPos = this.extractCommon(bestPath[0], newString, oldString, 0, options);
|
|
50
|
-
if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
|
|
51
|
-
// Identity per the equality and tokenizer
|
|
52
|
-
return done(buildValues(self, bestPath[0].lastComponent, newString, oldString, self.useLongestToken));
|
|
144
|
+
addToPath(path, added, removed, oldPosInc, options) {
|
|
145
|
+
const last = path.lastComponent;
|
|
146
|
+
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
147
|
+
return {
|
|
148
|
+
oldPos: path.oldPos + oldPosInc,
|
|
149
|
+
lastComponent: { count: last.count + 1, added: added, removed: removed, previousComponent: last.previousComponent }
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
return {
|
|
154
|
+
oldPos: path.oldPos + oldPosInc,
|
|
155
|
+
lastComponent: { count: 1, added: added, removed: removed, previousComponent: last }
|
|
156
|
+
};
|
|
157
|
+
}
|
|
53
158
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
// This optimization is not faithful to the original algorithm presented in
|
|
65
|
-
// Myers's paper, which instead pointlessly extends D-paths off the end of
|
|
66
|
-
// the edit graph - see page 7 of Myers's paper which notes this point
|
|
67
|
-
// explicitly and illustrates it with a diagram. This has major performance
|
|
68
|
-
// implications for some common scenarios. For instance, to compute a diff
|
|
69
|
-
// where the new text simply appends d characters on the end of the
|
|
70
|
-
// original text of length n, the true Myers algorithm will take O(n+d^2)
|
|
71
|
-
// time while this optimization needs only O(n+d) time.
|
|
72
|
-
var minDiagonalToConsider = -Infinity,
|
|
73
|
-
maxDiagonalToConsider = Infinity;
|
|
74
|
-
|
|
75
|
-
// Main worker method. checks all permutations of a given edit length for acceptance.
|
|
76
|
-
function execEditLength() {
|
|
77
|
-
for (var diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
|
|
78
|
-
var basePath = void 0;
|
|
79
|
-
var removePath = bestPath[diagonalPath - 1],
|
|
80
|
-
addPath = bestPath[diagonalPath + 1];
|
|
81
|
-
if (removePath) {
|
|
82
|
-
// No one else is going to attempt to use this value, clear it
|
|
83
|
-
bestPath[diagonalPath - 1] = undefined;
|
|
84
|
-
}
|
|
85
|
-
var canAdd = false;
|
|
86
|
-
if (addPath) {
|
|
87
|
-
// what newPos will be after we do an insertion:
|
|
88
|
-
var addPathNewPos = addPath.oldPos - diagonalPath;
|
|
89
|
-
canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
|
|
90
|
-
}
|
|
91
|
-
var canRemove = removePath && removePath.oldPos + 1 < oldLen;
|
|
92
|
-
if (!canAdd && !canRemove) {
|
|
93
|
-
// If this path is a terminal then prune
|
|
94
|
-
bestPath[diagonalPath] = undefined;
|
|
95
|
-
continue;
|
|
159
|
+
extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
|
|
160
|
+
const newLen = newTokens.length, oldLen = oldTokens.length;
|
|
161
|
+
let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
|
|
162
|
+
while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
|
|
163
|
+
newPos++;
|
|
164
|
+
oldPos++;
|
|
165
|
+
commonCount++;
|
|
166
|
+
if (options.oneChangePerToken) {
|
|
167
|
+
basePath.lastComponent = { count: 1, previousComponent: basePath.lastComponent, added: false, removed: false };
|
|
168
|
+
}
|
|
96
169
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
170
|
+
if (commonCount && !options.oneChangePerToken) {
|
|
171
|
+
basePath.lastComponent = { count: commonCount, previousComponent: basePath.lastComponent, added: false, removed: false };
|
|
172
|
+
}
|
|
173
|
+
basePath.oldPos = oldPos;
|
|
174
|
+
return newPos;
|
|
175
|
+
}
|
|
176
|
+
equals(left, right, options) {
|
|
177
|
+
if (options.comparator) {
|
|
178
|
+
return options.comparator(left, right);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
return left === right
|
|
182
|
+
|| (!!options.ignoreCase && left.toLowerCase() === right.toLowerCase());
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
removeEmpty(array) {
|
|
186
|
+
const ret = [];
|
|
187
|
+
for (let i = 0; i < array.length; i++) {
|
|
188
|
+
if (array[i]) {
|
|
189
|
+
ret.push(array[i]);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return ret;
|
|
193
|
+
}
|
|
194
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
195
|
+
castInput(value, options) {
|
|
196
|
+
return value;
|
|
197
|
+
}
|
|
198
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
199
|
+
tokenize(value, options) {
|
|
200
|
+
return Array.from(value);
|
|
201
|
+
}
|
|
202
|
+
join(chars) {
|
|
203
|
+
// Assumes ValueT is string, which is the case for most subclasses.
|
|
204
|
+
// When it's false, e.g. in diffArrays, this method needs to be overridden (e.g. with a no-op)
|
|
205
|
+
// Yes, the casts are verbose and ugly, because this pattern - of having the base class SORT OF
|
|
206
|
+
// assume tokens and values are strings, but not completely - is weird and janky.
|
|
207
|
+
return chars.join('');
|
|
208
|
+
}
|
|
209
|
+
postProcess(changeObjects,
|
|
210
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
211
|
+
options) {
|
|
212
|
+
return changeObjects;
|
|
213
|
+
}
|
|
214
|
+
get useLongestToken() {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
buildValues(lastComponent, newTokens, oldTokens) {
|
|
218
|
+
// First we convert our linked list of components in reverse order to an
|
|
219
|
+
// array in the right order:
|
|
220
|
+
const components = [];
|
|
221
|
+
let nextComponent;
|
|
222
|
+
while (lastComponent) {
|
|
223
|
+
components.push(lastComponent);
|
|
224
|
+
nextComponent = lastComponent.previousComponent;
|
|
225
|
+
delete lastComponent.previousComponent;
|
|
226
|
+
lastComponent = nextComponent;
|
|
227
|
+
}
|
|
228
|
+
components.reverse();
|
|
229
|
+
const componentLen = components.length;
|
|
230
|
+
let componentPos = 0, newPos = 0, oldPos = 0;
|
|
231
|
+
for (; componentPos < componentLen; componentPos++) {
|
|
232
|
+
const component = components[componentPos];
|
|
233
|
+
if (!component.removed) {
|
|
234
|
+
if (!component.added && this.useLongestToken) {
|
|
235
|
+
let value = newTokens.slice(newPos, newPos + component.count);
|
|
236
|
+
value = value.map(function (value, i) {
|
|
237
|
+
const oldValue = oldTokens[oldPos + i];
|
|
238
|
+
return oldValue.length > value.length ? oldValue : value;
|
|
239
|
+
});
|
|
240
|
+
component.value = this.join(value);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
component.value = this.join(newTokens.slice(newPos, newPos + component.count));
|
|
244
|
+
}
|
|
245
|
+
newPos += component.count;
|
|
246
|
+
// Common case
|
|
247
|
+
if (!component.added) {
|
|
248
|
+
oldPos += component.count;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
|
|
253
|
+
oldPos += component.count;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return components;
|
|
121
257
|
}
|
|
122
|
-
|
|
123
|
-
// Performs the length of edit iteration. Is a bit fugly as this has to support the
|
|
124
|
-
// sync and async mode which is never fun. Loops over execEditLength until a value
|
|
125
|
-
// is produced, or until the edit length exceeds options.maxEditLength (if given),
|
|
126
|
-
// in which case it will return undefined.
|
|
127
|
-
if (callback) {
|
|
128
|
-
(function exec() {
|
|
129
|
-
setTimeout(function () {
|
|
130
|
-
if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
|
|
131
|
-
return callback();
|
|
132
|
-
}
|
|
133
|
-
if (!execEditLength()) {
|
|
134
|
-
exec();
|
|
135
|
-
}
|
|
136
|
-
}, 0);
|
|
137
|
-
})();
|
|
138
|
-
} else {
|
|
139
|
-
while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
|
|
140
|
-
var ret = execEditLength();
|
|
141
|
-
if (ret) {
|
|
142
|
-
return ret;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
},
|
|
147
|
-
addToPath: function addToPath(path, added, removed, oldPosInc, options) {
|
|
148
|
-
var last = path.lastComponent;
|
|
149
|
-
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
150
|
-
return {
|
|
151
|
-
oldPos: path.oldPos + oldPosInc,
|
|
152
|
-
lastComponent: {
|
|
153
|
-
count: last.count + 1,
|
|
154
|
-
added: added,
|
|
155
|
-
removed: removed,
|
|
156
|
-
previousComponent: last.previousComponent
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
} else {
|
|
160
|
-
return {
|
|
161
|
-
oldPos: path.oldPos + oldPosInc,
|
|
162
|
-
lastComponent: {
|
|
163
|
-
count: 1,
|
|
164
|
-
added: added,
|
|
165
|
-
removed: removed,
|
|
166
|
-
previousComponent: last
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
},
|
|
171
|
-
extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath, options) {
|
|
172
|
-
var newLen = newString.length,
|
|
173
|
-
oldLen = oldString.length,
|
|
174
|
-
oldPos = basePath.oldPos,
|
|
175
|
-
newPos = oldPos - diagonalPath,
|
|
176
|
-
commonCount = 0;
|
|
177
|
-
while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldString[oldPos + 1], newString[newPos + 1], options)) {
|
|
178
|
-
newPos++;
|
|
179
|
-
oldPos++;
|
|
180
|
-
commonCount++;
|
|
181
|
-
if (options.oneChangePerToken) {
|
|
182
|
-
basePath.lastComponent = {
|
|
183
|
-
count: 1,
|
|
184
|
-
previousComponent: basePath.lastComponent,
|
|
185
|
-
added: false,
|
|
186
|
-
removed: false
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
if (commonCount && !options.oneChangePerToken) {
|
|
191
|
-
basePath.lastComponent = {
|
|
192
|
-
count: commonCount,
|
|
193
|
-
previousComponent: basePath.lastComponent,
|
|
194
|
-
added: false,
|
|
195
|
-
removed: false
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
basePath.oldPos = oldPos;
|
|
199
|
-
return newPos;
|
|
200
|
-
},
|
|
201
|
-
equals: function equals(left, right, options) {
|
|
202
|
-
if (options.comparator) {
|
|
203
|
-
return options.comparator(left, right);
|
|
204
|
-
} else {
|
|
205
|
-
return left === right || options.ignoreCase && left.toLowerCase() === right.toLowerCase();
|
|
206
|
-
}
|
|
207
|
-
},
|
|
208
|
-
removeEmpty: function removeEmpty(array) {
|
|
209
|
-
var ret = [];
|
|
210
|
-
for (var i = 0; i < array.length; i++) {
|
|
211
|
-
if (array[i]) {
|
|
212
|
-
ret.push(array[i]);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
return ret;
|
|
216
|
-
},
|
|
217
|
-
castInput: function castInput(value) {
|
|
218
|
-
return value;
|
|
219
|
-
},
|
|
220
|
-
tokenize: function tokenize(value) {
|
|
221
|
-
return Array.from(value);
|
|
222
|
-
},
|
|
223
|
-
join: function join(chars) {
|
|
224
|
-
return chars.join('');
|
|
225
|
-
},
|
|
226
|
-
postProcess: function postProcess(changeObjects) {
|
|
227
|
-
return changeObjects;
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
function buildValues(diff, lastComponent, newString, oldString, useLongestToken) {
|
|
231
|
-
// First we convert our linked list of components in reverse order to an
|
|
232
|
-
// array in the right order:
|
|
233
|
-
var components = [];
|
|
234
|
-
var nextComponent;
|
|
235
|
-
while (lastComponent) {
|
|
236
|
-
components.push(lastComponent);
|
|
237
|
-
nextComponent = lastComponent.previousComponent;
|
|
238
|
-
delete lastComponent.previousComponent;
|
|
239
|
-
lastComponent = nextComponent;
|
|
240
|
-
}
|
|
241
|
-
components.reverse();
|
|
242
|
-
var componentPos = 0,
|
|
243
|
-
componentLen = components.length,
|
|
244
|
-
newPos = 0,
|
|
245
|
-
oldPos = 0;
|
|
246
|
-
for (; componentPos < componentLen; componentPos++) {
|
|
247
|
-
var component = components[componentPos];
|
|
248
|
-
if (!component.removed) {
|
|
249
|
-
if (!component.added && useLongestToken) {
|
|
250
|
-
var value = newString.slice(newPos, newPos + component.count);
|
|
251
|
-
value = value.map(function (value, i) {
|
|
252
|
-
var oldValue = oldString[oldPos + i];
|
|
253
|
-
return oldValue.length > value.length ? oldValue : value;
|
|
254
|
-
});
|
|
255
|
-
component.value = diff.join(value);
|
|
256
|
-
} else {
|
|
257
|
-
component.value = diff.join(newString.slice(newPos, newPos + component.count));
|
|
258
|
-
}
|
|
259
|
-
newPos += component.count;
|
|
260
|
-
|
|
261
|
-
// Common case
|
|
262
|
-
if (!component.added) {
|
|
263
|
-
oldPos += component.count;
|
|
264
|
-
}
|
|
265
|
-
} else {
|
|
266
|
-
component.value = diff.join(oldString.slice(oldPos, oldPos + component.count));
|
|
267
|
-
oldPos += component.count;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
return components;
|
|
271
258
|
}
|
|
272
259
|
|
|
273
260
|
function longestCommonPrefix(str1, str2) {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
261
|
+
let i;
|
|
262
|
+
for (i = 0; i < str1.length && i < str2.length; i++) {
|
|
263
|
+
if (str1[i] != str2[i]) {
|
|
264
|
+
return str1.slice(0, i);
|
|
265
|
+
}
|
|
278
266
|
}
|
|
279
|
-
|
|
280
|
-
return str1.slice(0, i);
|
|
267
|
+
return str1.slice(0, i);
|
|
281
268
|
}
|
|
282
269
|
function longestCommonSuffix(str1, str2) {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
return str1.slice(-i);
|
|
270
|
+
let i;
|
|
271
|
+
// Unlike longestCommonPrefix, we need a special case to handle all scenarios
|
|
272
|
+
// where we return the empty string since str1.slice(-0) will return the
|
|
273
|
+
// entire string.
|
|
274
|
+
if (!str1 || !str2 || str1[str1.length - 1] != str2[str2.length - 1]) {
|
|
275
|
+
return '';
|
|
276
|
+
}
|
|
277
|
+
for (i = 0; i < str1.length && i < str2.length; i++) {
|
|
278
|
+
if (str1[str1.length - (i + 1)] != str2[str2.length - (i + 1)]) {
|
|
279
|
+
return str1.slice(-i);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return str1.slice(-i);
|
|
297
283
|
}
|
|
298
284
|
function replacePrefix(string, oldPrefix, newPrefix) {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
285
|
+
if (string.slice(0, oldPrefix.length) != oldPrefix) {
|
|
286
|
+
throw Error(`string ${JSON.stringify(string)} doesn't start with prefix ${JSON.stringify(oldPrefix)}; this is a bug`);
|
|
287
|
+
}
|
|
288
|
+
return newPrefix + string.slice(oldPrefix.length);
|
|
303
289
|
}
|
|
304
290
|
function replaceSuffix(string, oldSuffix, newSuffix) {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
291
|
+
if (!oldSuffix) {
|
|
292
|
+
return string + newSuffix;
|
|
293
|
+
}
|
|
294
|
+
if (string.slice(-oldSuffix.length) != oldSuffix) {
|
|
295
|
+
throw Error(`string ${JSON.stringify(string)} doesn't end with suffix ${JSON.stringify(oldSuffix)}; this is a bug`);
|
|
296
|
+
}
|
|
297
|
+
return string.slice(0, -oldSuffix.length) + newSuffix;
|
|
312
298
|
}
|
|
313
299
|
function removePrefix(string, oldPrefix) {
|
|
314
|
-
|
|
300
|
+
return replacePrefix(string, oldPrefix, '');
|
|
315
301
|
}
|
|
316
302
|
function removeSuffix(string, oldSuffix) {
|
|
317
|
-
|
|
303
|
+
return replaceSuffix(string, oldSuffix, '');
|
|
318
304
|
}
|
|
319
305
|
function maximumOverlap(string1, string2) {
|
|
320
|
-
|
|
306
|
+
return string2.slice(0, overlapCount(string1, string2));
|
|
321
307
|
}
|
|
322
|
-
|
|
323
308
|
// Nicked from https://stackoverflow.com/a/60422853/1709587
|
|
324
309
|
function overlapCount(a, b) {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
310
|
+
// Deal with cases where the strings differ in length
|
|
311
|
+
let startA = 0;
|
|
312
|
+
if (a.length > b.length) {
|
|
313
|
+
startA = a.length - b.length;
|
|
314
|
+
}
|
|
315
|
+
let endB = b.length;
|
|
316
|
+
if (a.length < b.length) {
|
|
317
|
+
endB = a.length;
|
|
318
|
+
}
|
|
319
|
+
// Create a back-reference for each index
|
|
320
|
+
// that should be followed in case of a mismatch.
|
|
321
|
+
// We only need B to make these references:
|
|
322
|
+
const map = Array(endB);
|
|
323
|
+
let k = 0; // Index that lags behind j
|
|
324
|
+
map[0] = 0;
|
|
325
|
+
for (let j = 1; j < endB; j++) {
|
|
326
|
+
if (b[j] == b[k]) {
|
|
327
|
+
map[j] = map[k]; // skip over the same character (optional optimisation)
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
map[j] = k;
|
|
331
|
+
}
|
|
332
|
+
while (k > 0 && b[j] != b[k]) {
|
|
333
|
+
k = map[k];
|
|
334
|
+
}
|
|
335
|
+
if (b[j] == b[k]) {
|
|
336
|
+
k++;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
// Phase 2: use these references while iterating over A
|
|
340
|
+
k = 0;
|
|
341
|
+
for (let i = startA; i < a.length; i++) {
|
|
342
|
+
while (k > 0 && a[i] != b[k]) {
|
|
343
|
+
k = map[k];
|
|
344
|
+
}
|
|
345
|
+
if (a[i] == b[k]) {
|
|
346
|
+
k++;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return k;
|
|
350
|
+
}
|
|
351
|
+
function trailingWs(string) {
|
|
352
|
+
// Yes, this looks overcomplicated and dumb - why not replace the whole function with
|
|
353
|
+
// return string.match(/\s*$/)[0]
|
|
354
|
+
// you ask? Because:
|
|
355
|
+
// 1. the trap described at https://markamery.com/blog/quadratic-time-regexes/ would mean doing
|
|
356
|
+
// this would cause this function to take O(n²) time in the worst case (specifically when
|
|
357
|
+
// there is a massive run of NON-TRAILING whitespace in `string`), and
|
|
358
|
+
// 2. the fix proposed in the same blog post, of using a negative lookbehind, is incompatible
|
|
359
|
+
// with old Safari versions that we'd like to not break if possible (see
|
|
360
|
+
// https://github.com/kpdecker/jsdiff/pull/550)
|
|
361
|
+
// It feels absurd to do this with an explicit loop instead of a regex, but I really can't see a
|
|
362
|
+
// better way that doesn't result in broken behaviour.
|
|
363
|
+
let i;
|
|
364
|
+
for (i = string.length - 1; i >= 0; i--) {
|
|
365
|
+
if (!string[i].match(/\s/)) {
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return string.substring(i + 1);
|
|
370
|
+
}
|
|
371
|
+
function leadingWs(string) {
|
|
372
|
+
// Thankfully the annoying considerations described in trailingWs don't apply here:
|
|
373
|
+
const match = string.match(/^\s*/);
|
|
374
|
+
return match ? match[0] : '';
|
|
364
375
|
}
|
|
365
376
|
|
|
366
377
|
// Based on https://en.wikipedia.org/wiki/Latin_script_in_Unicode
|
|
367
378
|
//
|
|
368
|
-
//
|
|
369
|
-
//
|
|
370
|
-
//
|
|
371
|
-
//
|
|
372
|
-
//
|
|
373
|
-
//
|
|
374
|
-
//
|
|
375
|
-
//
|
|
376
|
-
//
|
|
377
|
-
//
|
|
378
|
-
//
|
|
379
|
-
//
|
|
380
|
-
//
|
|
381
|
-
//
|
|
382
|
-
//
|
|
383
|
-
//
|
|
384
|
-
|
|
385
|
-
|
|
379
|
+
// Chars/ranges counted as "word" characters by this regex are as follows:
|
|
380
|
+
//
|
|
381
|
+
// + U+00AD Soft hyphen
|
|
382
|
+
// + 00C0–00FF (letters with diacritics from the Latin-1 Supplement), except:
|
|
383
|
+
// - U+00D7 × Multiplication sign
|
|
384
|
+
// - U+00F7 ÷ Division sign
|
|
385
|
+
// + Latin Extended-A, 0100–017F
|
|
386
|
+
// + Latin Extended-B, 0180–024F
|
|
387
|
+
// + IPA Extensions, 0250–02AF
|
|
388
|
+
// + Spacing Modifier Letters, 02B0–02FF, except:
|
|
389
|
+
// - U+02C7 ˇ ˇ Caron
|
|
390
|
+
// - U+02D8 ˘ ˘ Breve
|
|
391
|
+
// - U+02D9 ˙ ˙ Dot Above
|
|
392
|
+
// - U+02DA ˚ ˚ Ring Above
|
|
393
|
+
// - U+02DB ˛ ˛ Ogonek
|
|
394
|
+
// - U+02DC ˜ ˜ Small Tilde
|
|
395
|
+
// - U+02DD ˝ ˝ Double Acute Accent
|
|
396
|
+
// + Latin Extended Additional, 1E00–1EFF
|
|
397
|
+
const extendedWordChars = 'a-zA-Z0-9_\\u{AD}\\u{C0}-\\u{D6}\\u{D8}-\\u{F6}\\u{F8}-\\u{2C6}\\u{2C8}-\\u{2D7}\\u{2DE}-\\u{2FF}\\u{1E00}-\\u{1EFF}';
|
|
386
398
|
// Each token is one of the following:
|
|
387
399
|
// - A punctuation mark plus the surrounding whitespace
|
|
388
400
|
// - A word plus the surrounding whitespace
|
|
389
|
-
// - Pure whitespace (but only in the special case where
|
|
401
|
+
// - Pure whitespace (but only in the special case where the entire text
|
|
390
402
|
// is just whitespace)
|
|
391
403
|
//
|
|
392
404
|
// We have to include surrounding whitespace in the tokens because the two
|
|
@@ -403,376 +415,298 @@ var extendedWordChars = "a-zA-Z0-9_\\u{C0}-\\u{FF}\\u{D8}-\\u{F6}\\u{F8}-\\u{2C6
|
|
|
403
415
|
//
|
|
404
416
|
// Keeping the surrounding whitespace of course has implications for .equals
|
|
405
417
|
// and .join, not just .tokenize.
|
|
406
|
-
|
|
407
418
|
// This regex does NOT fully implement the tokenization rules described above.
|
|
408
419
|
// Instead, it gives runs of whitespace their own "token". The tokenize method
|
|
409
420
|
// then handles stitching whitespace tokens onto adjacent word or punctuation
|
|
410
421
|
// tokens.
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
422
|
+
const tokenizeIncludingWhitespace = new RegExp(`[${extendedWordChars}]+|\\s+|[^${extendedWordChars}]`, 'ug');
|
|
423
|
+
class WordDiff extends Diff {
|
|
424
|
+
equals(left, right, options) {
|
|
425
|
+
if (options.ignoreCase) {
|
|
426
|
+
left = left.toLowerCase();
|
|
427
|
+
right = right.toLowerCase();
|
|
428
|
+
}
|
|
429
|
+
return left.trim() === right.trim();
|
|
430
|
+
}
|
|
431
|
+
tokenize(value, options = {}) {
|
|
432
|
+
let parts;
|
|
433
|
+
if (options.intlSegmenter) {
|
|
434
|
+
const segmenter = options.intlSegmenter;
|
|
435
|
+
if (segmenter.resolvedOptions().granularity != 'word') {
|
|
436
|
+
throw new Error('The segmenter passed must have a granularity of "word"');
|
|
437
|
+
}
|
|
438
|
+
// We want `parts` to be an array whose elements alternate between being
|
|
439
|
+
// pure whitespace and being pure non-whitespace. This is ALMOST what the
|
|
440
|
+
// segments returned by a word-based Intl.Segmenter already look like,
|
|
441
|
+
// and therefore we can ALMOST get what we want by simply doing...
|
|
442
|
+
// parts = Array.from(segmenter.segment(value), segment => segment.segment);
|
|
443
|
+
// ... but not QUITE, because there's of one annoying special case: every
|
|
444
|
+
// newline character gets its own segment, instead of sharing a segment
|
|
445
|
+
// with other surrounding whitespace. We therefore need to manually merge
|
|
446
|
+
// consecutive segments of whitespace into a single part:
|
|
447
|
+
parts = [];
|
|
448
|
+
for (const segmentObj of Array.from(segmenter.segment(value))) {
|
|
449
|
+
const segment = segmentObj.segment;
|
|
450
|
+
if (parts.length && (/\s/).test(parts[parts.length - 1]) && (/\s/).test(segment)) {
|
|
451
|
+
parts[parts.length - 1] += segment;
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
parts.push(segment);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
parts = value.match(tokenizeIncludingWhitespace) || [];
|
|
460
|
+
}
|
|
461
|
+
const tokens = [];
|
|
462
|
+
let prevPart = null;
|
|
463
|
+
parts.forEach(part => {
|
|
464
|
+
if ((/\s/).test(part)) {
|
|
465
|
+
if (prevPart == null) {
|
|
466
|
+
tokens.push(part);
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
tokens.push(tokens.pop() + part);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
else if (prevPart != null && (/\s/).test(prevPart)) {
|
|
473
|
+
if (tokens[tokens.length - 1] == prevPart) {
|
|
474
|
+
tokens.push(tokens.pop() + part);
|
|
475
|
+
}
|
|
476
|
+
else {
|
|
477
|
+
tokens.push(prevPart + part);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
tokens.push(part);
|
|
482
|
+
}
|
|
483
|
+
prevPart = part;
|
|
484
|
+
});
|
|
485
|
+
return tokens;
|
|
486
|
+
}
|
|
487
|
+
join(tokens) {
|
|
488
|
+
// Tokens being joined here will always have appeared consecutively in the
|
|
489
|
+
// same text, so we can simply strip off the leading whitespace from all the
|
|
490
|
+
// tokens except the first (and except any whitespace-only tokens - but such
|
|
491
|
+
// a token will always be the first and only token anyway) and then join them
|
|
492
|
+
// and the whitespace around words and punctuation will end up correct.
|
|
493
|
+
return tokens.map((token, i) => {
|
|
494
|
+
if (i == 0) {
|
|
495
|
+
return token;
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
return token.replace((/^\s+/), '');
|
|
499
|
+
}
|
|
500
|
+
}).join('');
|
|
501
|
+
}
|
|
502
|
+
postProcess(changes, options) {
|
|
503
|
+
if (!changes || options.oneChangePerToken) {
|
|
504
|
+
return changes;
|
|
505
|
+
}
|
|
506
|
+
let lastKeep = null;
|
|
507
|
+
// Change objects representing any insertion or deletion since the last
|
|
508
|
+
// "keep" change object. There can be at most one of each.
|
|
509
|
+
let insertion = null;
|
|
510
|
+
let deletion = null;
|
|
511
|
+
changes.forEach(change => {
|
|
512
|
+
if (change.added) {
|
|
513
|
+
insertion = change;
|
|
514
|
+
}
|
|
515
|
+
else if (change.removed) {
|
|
516
|
+
deletion = change;
|
|
517
|
+
}
|
|
518
|
+
else {
|
|
519
|
+
if (insertion || deletion) { // May be false at start of text
|
|
520
|
+
dedupeWhitespaceInChangeObjects(lastKeep, deletion, insertion, change);
|
|
521
|
+
}
|
|
522
|
+
lastKeep = change;
|
|
523
|
+
insertion = null;
|
|
524
|
+
deletion = null;
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
if (insertion || deletion) {
|
|
528
|
+
dedupeWhitespaceInChangeObjects(lastKeep, deletion, insertion, null);
|
|
529
|
+
}
|
|
530
|
+
return changes;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
const wordDiff = new WordDiff();
|
|
498
534
|
function diffWords(oldStr, newStr, options) {
|
|
499
|
-
|
|
535
|
+
return wordDiff.diff(oldStr, newStr, options);
|
|
500
536
|
}
|
|
501
537
|
function dedupeWhitespaceInChangeObjects(startKeep, deletion, insertion, endKeep) {
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
538
|
+
// Before returning, we tidy up the leading and trailing whitespace of the
|
|
539
|
+
// change objects to eliminate cases where trailing whitespace in one object
|
|
540
|
+
// is repeated as leading whitespace in the next.
|
|
541
|
+
// Below are examples of the outcomes we want here to explain the code.
|
|
542
|
+
// I=insert, K=keep, D=delete
|
|
543
|
+
// 1. diffing 'foo bar baz' vs 'foo baz'
|
|
544
|
+
// Prior to cleanup, we have K:'foo ' D:' bar ' K:' baz'
|
|
545
|
+
// After cleanup, we want: K:'foo ' D:'bar ' K:'baz'
|
|
546
|
+
//
|
|
547
|
+
// 2. Diffing 'foo bar baz' vs 'foo qux baz'
|
|
548
|
+
// Prior to cleanup, we have K:'foo ' D:' bar ' I:' qux ' K:' baz'
|
|
549
|
+
// After cleanup, we want K:'foo ' D:'bar' I:'qux' K:' baz'
|
|
550
|
+
//
|
|
551
|
+
// 3. Diffing 'foo\nbar baz' vs 'foo baz'
|
|
552
|
+
// Prior to cleanup, we have K:'foo ' D:'\nbar ' K:' baz'
|
|
553
|
+
// After cleanup, we want K'foo' D:'\nbar' K:' baz'
|
|
554
|
+
//
|
|
555
|
+
// 4. Diffing 'foo baz' vs 'foo\nbar baz'
|
|
556
|
+
// Prior to cleanup, we have K:'foo\n' I:'\nbar ' K:' baz'
|
|
557
|
+
// After cleanup, we ideally want K'foo' I:'\nbar' K:' baz'
|
|
558
|
+
// but don't actually manage this currently (the pre-cleanup change
|
|
559
|
+
// objects don't contain enough information to make it possible).
|
|
560
|
+
//
|
|
561
|
+
// 5. Diffing 'foo bar baz' vs 'foo baz'
|
|
562
|
+
// Prior to cleanup, we have K:'foo ' D:' bar ' K:' baz'
|
|
563
|
+
// After cleanup, we want K:'foo ' D:' bar ' K:'baz'
|
|
564
|
+
//
|
|
565
|
+
// Our handling is unavoidably imperfect in the case where there's a single
|
|
566
|
+
// indel between keeps and the whitespace has changed. For instance, consider
|
|
567
|
+
// diffing 'foo\tbar\nbaz' vs 'foo baz'. Unless we create an extra change
|
|
568
|
+
// object to represent the insertion of the space character (which isn't even
|
|
569
|
+
// a token), we have no way to avoid losing information about the texts'
|
|
570
|
+
// original whitespace in the result we return. Still, we do our best to
|
|
571
|
+
// output something that will look sensible if we e.g. print it with
|
|
572
|
+
// insertions in green and deletions in red.
|
|
573
|
+
// Between two "keep" change objects (or before the first or after the last
|
|
574
|
+
// change object), we can have either:
|
|
575
|
+
// * A "delete" followed by an "insert"
|
|
576
|
+
// * Just an "insert"
|
|
577
|
+
// * Just a "delete"
|
|
578
|
+
// We handle the three cases separately.
|
|
579
|
+
if (deletion && insertion) {
|
|
580
|
+
const oldWsPrefix = leadingWs(deletion.value);
|
|
581
|
+
const oldWsSuffix = trailingWs(deletion.value);
|
|
582
|
+
const newWsPrefix = leadingWs(insertion.value);
|
|
583
|
+
const newWsSuffix = trailingWs(insertion.value);
|
|
584
|
+
if (startKeep) {
|
|
585
|
+
const commonWsPrefix = longestCommonPrefix(oldWsPrefix, newWsPrefix);
|
|
586
|
+
startKeep.value = replaceSuffix(startKeep.value, newWsPrefix, commonWsPrefix);
|
|
587
|
+
deletion.value = removePrefix(deletion.value, commonWsPrefix);
|
|
588
|
+
insertion.value = removePrefix(insertion.value, commonWsPrefix);
|
|
589
|
+
}
|
|
590
|
+
if (endKeep) {
|
|
591
|
+
const commonWsSuffix = longestCommonSuffix(oldWsSuffix, newWsSuffix);
|
|
592
|
+
endKeep.value = replacePrefix(endKeep.value, newWsSuffix, commonWsSuffix);
|
|
593
|
+
deletion.value = removeSuffix(deletion.value, commonWsSuffix);
|
|
594
|
+
insertion.value = removeSuffix(insertion.value, commonWsSuffix);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
else if (insertion) {
|
|
598
|
+
// The whitespaces all reflect what was in the new text rather than
|
|
599
|
+
// the old, so we essentially have no information about whitespace
|
|
600
|
+
// insertion or deletion. We just want to dedupe the whitespace.
|
|
601
|
+
// We do that by having each change object keep its trailing
|
|
602
|
+
// whitespace and deleting duplicate leading whitespace where
|
|
603
|
+
// present.
|
|
604
|
+
if (startKeep) {
|
|
605
|
+
const ws = leadingWs(insertion.value);
|
|
606
|
+
insertion.value = insertion.value.substring(ws.length);
|
|
607
|
+
}
|
|
608
|
+
if (endKeep) {
|
|
609
|
+
const ws = leadingWs(endKeep.value);
|
|
610
|
+
endKeep.value = endKeep.value.substring(ws.length);
|
|
611
|
+
}
|
|
612
|
+
// otherwise we've got a deletion and no insertion
|
|
613
|
+
}
|
|
614
|
+
else if (startKeep && endKeep) {
|
|
615
|
+
const newWsFull = leadingWs(endKeep.value), delWsStart = leadingWs(deletion.value), delWsEnd = trailingWs(deletion.value);
|
|
616
|
+
// Any whitespace that comes straight after startKeep in both the old and
|
|
617
|
+
// new texts, assign to startKeep and remove from the deletion.
|
|
618
|
+
const newWsStart = longestCommonPrefix(newWsFull, delWsStart);
|
|
619
|
+
deletion.value = removePrefix(deletion.value, newWsStart);
|
|
620
|
+
// Any whitespace that comes straight before endKeep in both the old and
|
|
621
|
+
// new texts, and hasn't already been assigned to startKeep, assign to
|
|
622
|
+
// endKeep and remove from the deletion.
|
|
623
|
+
const newWsEnd = longestCommonSuffix(removePrefix(newWsFull, newWsStart), delWsEnd);
|
|
624
|
+
deletion.value = removeSuffix(deletion.value, newWsEnd);
|
|
625
|
+
endKeep.value = replacePrefix(endKeep.value, newWsFull, newWsEnd);
|
|
626
|
+
// If there's any whitespace from the new text that HASN'T already been
|
|
627
|
+
// assigned, assign it to the start:
|
|
628
|
+
startKeep.value = replaceSuffix(startKeep.value, newWsFull, newWsFull.slice(0, newWsFull.length - newWsEnd.length));
|
|
629
|
+
}
|
|
630
|
+
else if (endKeep) {
|
|
631
|
+
// We are at the start of the text. Preserve all the whitespace on
|
|
632
|
+
// endKeep, and just remove whitespace from the end of deletion to the
|
|
633
|
+
// extent that it overlaps with the start of endKeep.
|
|
634
|
+
const endKeepWsPrefix = leadingWs(endKeep.value);
|
|
635
|
+
const deletionWsSuffix = trailingWs(deletion.value);
|
|
636
|
+
const overlap = maximumOverlap(deletionWsSuffix, endKeepWsPrefix);
|
|
637
|
+
deletion.value = removeSuffix(deletion.value, overlap);
|
|
638
|
+
}
|
|
639
|
+
else if (startKeep) {
|
|
640
|
+
// We are at the END of the text. Preserve all the whitespace on
|
|
641
|
+
// startKeep, and just remove whitespace from the start of deletion to
|
|
642
|
+
// the extent that it overlaps with the end of startKeep.
|
|
643
|
+
const startKeepWsSuffix = trailingWs(startKeep.value);
|
|
644
|
+
const deletionWsPrefix = leadingWs(deletion.value);
|
|
645
|
+
const overlap = maximumOverlap(startKeepWsSuffix, deletionWsPrefix);
|
|
646
|
+
deletion.value = removePrefix(deletion.value, overlap);
|
|
647
|
+
}
|
|
612
648
|
}
|
|
613
|
-
var wordWithSpaceDiff = new Diff();
|
|
614
|
-
wordWithSpaceDiff.tokenize = function (value) {
|
|
615
|
-
// Slightly different to the tokenizeIncludingWhitespace regex used above in
|
|
616
|
-
// that this one treats each individual newline as a distinct tokens, rather
|
|
617
|
-
// than merging them into other surrounding whitespace. This was requested
|
|
618
|
-
// in https://github.com/kpdecker/jsdiff/issues/180 &
|
|
619
|
-
// https://github.com/kpdecker/jsdiff/issues/211
|
|
620
|
-
var regex = new RegExp("(\\r?\\n)|[".concat(extendedWordChars, "]+|[^\\S\\n\\r]+|[^").concat(extendedWordChars, "]"), 'ug');
|
|
621
|
-
return value.match(regex) || [];
|
|
622
|
-
};
|
|
623
|
-
|
|
624
|
-
var lineDiff = new Diff();
|
|
625
|
-
lineDiff.tokenize = function (value, options) {
|
|
626
|
-
if (options.stripTrailingCr) {
|
|
627
|
-
// remove one \r before \n to match GNU diff's --strip-trailing-cr behavior
|
|
628
|
-
value = value.replace(/\r\n/g, '\n');
|
|
629
|
-
}
|
|
630
|
-
var retLines = [],
|
|
631
|
-
linesAndNewlines = value.split(/(\n|\r\n)/);
|
|
632
649
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
}
|
|
664
|
-
} else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
|
|
665
|
-
if (left.endsWith('\n')) {
|
|
666
|
-
left = left.slice(0, -1);
|
|
667
|
-
}
|
|
668
|
-
if (right.endsWith('\n')) {
|
|
669
|
-
right = right.slice(0, -1);
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
return Diff.prototype.equals.call(this, left, right, options);
|
|
673
|
-
};
|
|
674
|
-
function diffLines(oldStr, newStr, callback) {
|
|
675
|
-
return lineDiff.diff(oldStr, newStr, callback);
|
|
650
|
+
class LineDiff extends Diff {
|
|
651
|
+
constructor() {
|
|
652
|
+
super(...arguments);
|
|
653
|
+
this.tokenize = tokenize$1;
|
|
654
|
+
}
|
|
655
|
+
equals(left, right, options) {
|
|
656
|
+
// If we're ignoring whitespace, we need to normalise lines by stripping
|
|
657
|
+
// whitespace before checking equality. (This has an annoying interaction
|
|
658
|
+
// with newlineIsToken that requires special handling: if newlines get their
|
|
659
|
+
// own token, then we DON'T want to trim the *newline* tokens down to empty
|
|
660
|
+
// strings, since this would cause us to treat whitespace-only line content
|
|
661
|
+
// as equal to a separator between lines, which would be weird and
|
|
662
|
+
// inconsistent with the documented behavior of the options.)
|
|
663
|
+
if (options.ignoreWhitespace) {
|
|
664
|
+
if (!options.newlineIsToken || !left.includes('\n')) {
|
|
665
|
+
left = left.trim();
|
|
666
|
+
}
|
|
667
|
+
if (!options.newlineIsToken || !right.includes('\n')) {
|
|
668
|
+
right = right.trim();
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
|
|
672
|
+
if (left.endsWith('\n')) {
|
|
673
|
+
left = left.slice(0, -1);
|
|
674
|
+
}
|
|
675
|
+
if (right.endsWith('\n')) {
|
|
676
|
+
right = right.slice(0, -1);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
return super.equals(left, right, options);
|
|
680
|
+
}
|
|
676
681
|
}
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
return value.split(/(\S.+?[.!?])(?=\s+|$)/);
|
|
681
|
-
};
|
|
682
|
-
|
|
683
|
-
var cssDiff = new Diff();
|
|
684
|
-
cssDiff.tokenize = function (value) {
|
|
685
|
-
return value.split(/([{}:;,]|\s+)/);
|
|
686
|
-
};
|
|
687
|
-
function _typeof(o) {
|
|
688
|
-
"@babel/helpers - typeof";
|
|
689
|
-
|
|
690
|
-
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
|
|
691
|
-
return typeof o;
|
|
692
|
-
} : function (o) {
|
|
693
|
-
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
694
|
-
}, _typeof(o);
|
|
682
|
+
const lineDiff = new LineDiff();
|
|
683
|
+
function diffLines(oldStr, newStr, options) {
|
|
684
|
+
return lineDiff.diff(oldStr, newStr, options);
|
|
695
685
|
}
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
//
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
replacementStack = replacementStack || [];
|
|
719
|
-
if (replacer) {
|
|
720
|
-
obj = replacer(key, obj);
|
|
721
|
-
}
|
|
722
|
-
var i;
|
|
723
|
-
for (i = 0; i < stack.length; i += 1) {
|
|
724
|
-
if (stack[i] === obj) {
|
|
725
|
-
return replacementStack[i];
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
var canonicalizedObj;
|
|
729
|
-
if ('[object Array]' === Object.prototype.toString.call(obj)) {
|
|
730
|
-
stack.push(obj);
|
|
731
|
-
canonicalizedObj = new Array(obj.length);
|
|
732
|
-
replacementStack.push(canonicalizedObj);
|
|
733
|
-
for (i = 0; i < obj.length; i += 1) {
|
|
734
|
-
canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack, replacer, key);
|
|
735
|
-
}
|
|
736
|
-
stack.pop();
|
|
737
|
-
replacementStack.pop();
|
|
738
|
-
return canonicalizedObj;
|
|
739
|
-
}
|
|
740
|
-
if (obj && obj.toJSON) {
|
|
741
|
-
obj = obj.toJSON();
|
|
742
|
-
}
|
|
743
|
-
if (_typeof(obj) === 'object' && obj !== null) {
|
|
744
|
-
stack.push(obj);
|
|
745
|
-
canonicalizedObj = {};
|
|
746
|
-
replacementStack.push(canonicalizedObj);
|
|
747
|
-
var sortedKeys = [],
|
|
748
|
-
_key;
|
|
749
|
-
for (_key in obj) {
|
|
750
|
-
/* istanbul ignore else */
|
|
751
|
-
if (Object.prototype.hasOwnProperty.call(obj, _key)) {
|
|
752
|
-
sortedKeys.push(_key);
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
sortedKeys.sort();
|
|
756
|
-
for (i = 0; i < sortedKeys.length; i += 1) {
|
|
757
|
-
_key = sortedKeys[i];
|
|
758
|
-
canonicalizedObj[_key] = canonicalize(obj[_key], stack, replacementStack, replacer, _key);
|
|
759
|
-
}
|
|
760
|
-
stack.pop();
|
|
761
|
-
replacementStack.pop();
|
|
762
|
-
} else {
|
|
763
|
-
canonicalizedObj = obj;
|
|
764
|
-
}
|
|
765
|
-
return canonicalizedObj;
|
|
686
|
+
// Exported standalone so it can be used from jsonDiff too.
|
|
687
|
+
function tokenize$1(value, options) {
|
|
688
|
+
if (options.stripTrailingCr) {
|
|
689
|
+
// remove one \r before \n to match GNU diff's --strip-trailing-cr behavior
|
|
690
|
+
value = value.replace(/\r\n/g, '\n');
|
|
691
|
+
}
|
|
692
|
+
const retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
|
|
693
|
+
// Ignore the final empty token that occurs if the string ends with a new line
|
|
694
|
+
if (!linesAndNewlines[linesAndNewlines.length - 1]) {
|
|
695
|
+
linesAndNewlines.pop();
|
|
696
|
+
}
|
|
697
|
+
// Merge the content and line separators into single tokens
|
|
698
|
+
for (let i = 0; i < linesAndNewlines.length; i++) {
|
|
699
|
+
const line = linesAndNewlines[i];
|
|
700
|
+
if (i % 2 && !options.newlineIsToken) {
|
|
701
|
+
retLines[retLines.length - 1] += line;
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
retLines.push(line);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
return retLines;
|
|
766
708
|
}
|
|
767
709
|
|
|
768
|
-
var arrayDiff = new Diff();
|
|
769
|
-
arrayDiff.tokenize = function (value) {
|
|
770
|
-
return value.slice();
|
|
771
|
-
};
|
|
772
|
-
arrayDiff.join = arrayDiff.removeEmpty = function (value) {
|
|
773
|
-
return value;
|
|
774
|
-
};
|
|
775
|
-
|
|
776
710
|
/**
|
|
777
711
|
* Compute a structured diff between two strings.
|
|
778
712
|
*
|