@atlaskit/editor-plugin-show-diff 12.1.1 → 12.1.3
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 +15 -0
- package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +41 -123
- package/dist/cjs/pm-plugins/calculateDiff/diffBySteps.js +1 -124
- package/dist/cjs/pm-plugins/decorations/createNodeChangedDecorationWidget.js +9 -1
- package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +9 -88
- package/dist/es2019/pm-plugins/calculateDiff/diffBySteps.js +0 -104
- package/dist/es2019/pm-plugins/decorations/createNodeChangedDecorationWidget.js +9 -1
- package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +42 -124
- package/dist/esm/pm-plugins/calculateDiff/diffBySteps.js +0 -123
- package/dist/esm/pm-plugins/decorations/createNodeChangedDecorationWidget.js +9 -1
- package/dist/types/pm-plugins/calculateDiff/diffBySteps.d.ts +0 -20
- package/package.json +6 -6
- package/dist/cjs/pm-plugins/decorations/createGranularBlockReferenceWidget.js +0 -125
- package/dist/es2019/pm-plugins/decorations/createGranularBlockReferenceWidget.js +0 -120
- package/dist/esm/pm-plugins/decorations/createGranularBlockReferenceWidget.js +0 -120
- package/dist/types/pm-plugins/decorations/createGranularBlockReferenceWidget.d.ts +0 -36
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-show-diff
|
|
2
2
|
|
|
3
|
+
## 12.1.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`774c01e2f1cdd`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/774c01e2f1cdd) -
|
|
8
|
+
Remove the `platform_editor_diff_granular_extended` experiment and retain its disabled behavior.
|
|
9
|
+
- Updated dependencies
|
|
10
|
+
|
|
11
|
+
## 12.1.2
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- [`3d45d0ef1fb3f`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/3d45d0ef1fb3f) -
|
|
16
|
+
Prevent block diff content from overlapping preceding editor nodes
|
|
17
|
+
|
|
3
18
|
## 12.1.1
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
|
@@ -19,7 +19,6 @@ var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
|
|
|
19
19
|
var _areDocsEqualByBlockStructureAndText = require("../areDocsEqualByBlockStructureAndText");
|
|
20
20
|
var _createAnchorDecorationWidgets = require("../decorations/createAnchorDecorationWidgets");
|
|
21
21
|
var _createBlockChangedDecoration = require("../decorations/createBlockChangedDecoration");
|
|
22
|
-
var _createGranularBlockReferenceWidget = require("../decorations/createGranularBlockReferenceWidget");
|
|
23
22
|
var _createInlineChangedDecoration = require("../decorations/createInlineChangedDecoration");
|
|
24
23
|
var _createNodeChangedDecorationWidget = require("../decorations/createNodeChangedDecorationWidget");
|
|
25
24
|
var _decorationKeys = require("../decorations/decorationKeys");
|
|
@@ -260,7 +259,7 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4
|
|
|
260
259
|
|
|
261
260
|
// Our default operations are insertions, so it should match the opposite of isInverted.
|
|
262
261
|
var isInserted = !isInverted;
|
|
263
|
-
var createDecorationsForChange = function createDecorationsForChange(change
|
|
262
|
+
var createDecorationsForChange = function createDecorationsForChange(change) {
|
|
264
263
|
var isActive = activeIndexPos && change.fromB === activeIndexPos.from && change.toB === activeIndexPos.to;
|
|
265
264
|
|
|
266
265
|
// Hoisted because it decides BOTH where the deleted widget is anchored and — since the
|
|
@@ -272,10 +271,8 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4
|
|
|
272
271
|
inlineDeletedDiffPlacement: inlineDeletedDiffPlacement
|
|
273
272
|
});
|
|
274
273
|
if (change.inserted.length > 0) {
|
|
275
|
-
//
|
|
276
|
-
|
|
277
|
-
// isInverted gates both — on an inverted diff the inserted side is visually the deleted side.
|
|
278
|
-
var shouldHideDeleted = (0, _isExtendedEnabled.isExtendedEnabled)(diffType) ? isInverted && (hideDeletedDiffs || showGranularWithBlock && change.deleted.length > 0) : false;
|
|
274
|
+
// On an inverted diff the inserted side is visually the deleted side.
|
|
275
|
+
var shouldHideDeleted = (0, _isExtendedEnabled.isExtendedEnabled)(diffType) ? isInverted && hideDeletedDiffs : false;
|
|
279
276
|
|
|
280
277
|
// For `smart` NODE-level promotions the change range spans a whole container
|
|
281
278
|
// (e.g. an entire list/table/layout, using outer node bounds). Applying a SINGLE
|
|
@@ -292,7 +289,7 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4
|
|
|
292
289
|
// change. Used to decide if indicator anchor positions should be adjusted
|
|
293
290
|
// inward — when the widget is present the anchor must stay at the block
|
|
294
291
|
// boundary to keep the indicator bar continuous with the deleted content.
|
|
295
|
-
var willRenderDeletedWidget = change.deleted.length > 0 && !((0, _isExtendedEnabled.isExtendedEnabled)(diffType) && !isInverted &&
|
|
292
|
+
var willRenderDeletedWidget = change.deleted.length > 0 && !((0, _isExtendedEnabled.isExtendedEnabled)(diffType) && !isInverted && hideDeletedDiffs && change.inserted.length > 0);
|
|
296
293
|
if (isSmartNodeLevel) {
|
|
297
294
|
var _iterator2 = _createForOfIteratorHelper(leafTextblockRanges(tr.doc, change.fromB, change.toB)),
|
|
298
295
|
_step2;
|
|
@@ -356,7 +353,7 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4
|
|
|
356
353
|
}))));
|
|
357
354
|
}
|
|
358
355
|
if (change.deleted.length > 0) {
|
|
359
|
-
var _shouldHideDeleted = (0, _isExtendedEnabled.isExtendedEnabled)(diffType) ? !isInverted &&
|
|
356
|
+
var _shouldHideDeleted = (0, _isExtendedEnabled.isExtendedEnabled)(diffType) ? !isInverted && hideDeletedDiffs && change.inserted.length > 0 : false;
|
|
360
357
|
if (!_shouldHideDeleted) {
|
|
361
358
|
decorations.push.apply(decorations, (0, _toConsumableArray2.default)((0, _createNodeChangedDecorationWidget.createNodeChangedDecorationWidget)(_objectSpread(_objectSpread({
|
|
362
359
|
change: change,
|
|
@@ -377,88 +374,9 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4
|
|
|
377
374
|
}
|
|
378
375
|
}
|
|
379
376
|
};
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
// apply the shouldHideDeleted suppression threshold (> 3 granular changes per step).
|
|
384
|
-
// getChanges returns a flat Change[] with no per-step grouping, making this count impossible
|
|
385
|
-
// to derive after the fact without re-introducing per-change metadata.
|
|
386
|
-
var stepChanges = (0, _diffBySteps.getStepChanges)(originalDoc, steps);
|
|
387
|
-
stepChanges.forEach(function (_ref5) {
|
|
388
|
-
var isGranular = _ref5.isGranular,
|
|
389
|
-
stepChangeList = _ref5.changes;
|
|
390
|
-
var granularCount = isGranular ? stepChangeList.length : 0;
|
|
391
|
-
|
|
392
|
-
// Calculate the average ratio of changed content on both A (original) and B (new)
|
|
393
|
-
// sides of the diff. If 30% or more of the block has changed on average, we show
|
|
394
|
-
// the block reference widget even if the granular change count is below the threshold.
|
|
395
|
-
// Block length is derived from the enclosing text block boundaries rather than the
|
|
396
|
-
// first/last change positions, so unchanged words at the start/end are accounted for.
|
|
397
|
-
var avgChangedRatio = 0;
|
|
398
|
-
if (isGranular && stepChangeList.length > 0) {
|
|
399
|
-
var first = stepChangeList[0];
|
|
400
|
-
var last = stepChangeList[stepChangeList.length - 1];
|
|
401
|
-
var resolvedA = originalDoc.resolve(first.fromA);
|
|
402
|
-
var resolvedB = tr.doc.resolve(first.fromB);
|
|
403
|
-
var blockStartA = first.fromA;
|
|
404
|
-
var blockEndA = last.toA;
|
|
405
|
-
var blockStartB = first.fromB;
|
|
406
|
-
var blockEndB = last.toB;
|
|
407
|
-
for (var depth = resolvedA.depth; depth >= 0; depth--) {
|
|
408
|
-
var node = resolvedA.node(depth);
|
|
409
|
-
if (node.isTextblock) {
|
|
410
|
-
blockStartA = resolvedA.start(depth);
|
|
411
|
-
blockEndA = blockStartA + node.nodeSize - 2; // exclude open/close tokens
|
|
412
|
-
break;
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
for (var _depth = resolvedB.depth; _depth >= 0; _depth--) {
|
|
416
|
-
var _node = resolvedB.node(_depth);
|
|
417
|
-
if (_node.isTextblock) {
|
|
418
|
-
blockStartB = resolvedB.start(_depth);
|
|
419
|
-
blockEndB = blockStartB + _node.nodeSize - 2; // exclude open/close tokens
|
|
420
|
-
break;
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
var blockLengthA = blockEndA - blockStartA;
|
|
424
|
-
var blockLengthB = blockEndB - blockStartB;
|
|
425
|
-
var totalChangedA = stepChangeList.reduce(function (sum, c) {
|
|
426
|
-
return sum + (c.toA - c.fromA);
|
|
427
|
-
}, 0);
|
|
428
|
-
var totalChangedB = stepChangeList.reduce(function (sum, c) {
|
|
429
|
-
return sum + (c.toB - c.fromB);
|
|
430
|
-
}, 0);
|
|
431
|
-
var ratioA = blockLengthA > 0 ? totalChangedA / blockLengthA : 0;
|
|
432
|
-
var ratioB = blockLengthB > 0 ? totalChangedB / blockLengthB : 0;
|
|
433
|
-
avgChangedRatio = (ratioA + ratioB) / 2;
|
|
434
|
-
}
|
|
435
|
-
var showGranularWithBlock = isGranular && granularCount !== 1 && (granularCount > 3 || avgChangedRatio >= 0.3);
|
|
436
|
-
stepChangeList.forEach(function (change) {
|
|
437
|
-
createDecorationsForChange(change, showGranularWithBlock);
|
|
438
|
-
});
|
|
439
|
-
if (showGranularWithBlock && stepChangeList.length > 0 && !hideDeletedDiffs) {
|
|
440
|
-
var lastChange = stepChangeList[stepChangeList.length - 1];
|
|
441
|
-
var granularBlockDiffId = crypto.randomUUID();
|
|
442
|
-
var blockWidgets = (0, _createGranularBlockReferenceWidget.createGranularBlockReferenceWidget)({
|
|
443
|
-
change: lastChange,
|
|
444
|
-
originalDoc: originalDoc,
|
|
445
|
-
newDoc: tr.doc,
|
|
446
|
-
isInverted: isInverted,
|
|
447
|
-
nodeViewSerializer: nodeViewSerializer,
|
|
448
|
-
colorScheme: colorScheme,
|
|
449
|
-
intl: intl,
|
|
450
|
-
activeIndexPos: activeIndexPos,
|
|
451
|
-
diffId: granularBlockDiffId,
|
|
452
|
-
showIndicators: showIndicators
|
|
453
|
-
});
|
|
454
|
-
decorations.push.apply(decorations, (0, _toConsumableArray2.default)(blockWidgets));
|
|
455
|
-
}
|
|
456
|
-
});
|
|
457
|
-
} else {
|
|
458
|
-
changes.forEach(function (change) {
|
|
459
|
-
createDecorationsForChange(change, /* showGranularWithBlock */false);
|
|
460
|
-
});
|
|
461
|
-
}
|
|
377
|
+
changes.forEach(function (change) {
|
|
378
|
+
createDecorationsForChange(change);
|
|
379
|
+
});
|
|
462
380
|
(0, _getMarkChangeRanges.getMarkChangeRanges)(steps).forEach(function (change) {
|
|
463
381
|
var isActive = activeIndexPos && change.fromB === activeIndexPos.from && change.toB === activeIndexPos.to;
|
|
464
382
|
decorations.push.apply(decorations, (0, _toConsumableArray2.default)((0, _createInlineChangedDecoration.createInlineChangedDecoration)({
|
|
@@ -530,42 +448,42 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4
|
|
|
530
448
|
};
|
|
531
449
|
var calculateDiffDecorations = exports.calculateDiffDecorations = (0, _memoizeOne.default)(calculateDiffDecorationsInner,
|
|
532
450
|
// Cache results unless relevant inputs change
|
|
533
|
-
function (
|
|
534
|
-
var
|
|
451
|
+
function (_ref5, _ref6) {
|
|
452
|
+
var _ref0;
|
|
453
|
+
var _ref7 = (0, _slicedToArray2.default)(_ref5, 1),
|
|
454
|
+
_ref7$ = _ref7[0],
|
|
455
|
+
pluginState = _ref7$.pluginState,
|
|
456
|
+
state = _ref7$.state,
|
|
457
|
+
colorScheme = _ref7$.colorScheme,
|
|
458
|
+
intl = _ref7$.intl,
|
|
459
|
+
activeIndexPos = _ref7$.activeIndexPos,
|
|
460
|
+
isInverted = _ref7$.isInverted,
|
|
461
|
+
diffType = _ref7$.diffType,
|
|
462
|
+
hideDeletedDiffs = _ref7$.hideDeletedDiffs,
|
|
463
|
+
hideAddedDiffsUnderline = _ref7$.hideAddedDiffsUnderline,
|
|
464
|
+
showIndicators = _ref7$.showIndicators,
|
|
465
|
+
smartThresholds = _ref7$.smartThresholds,
|
|
466
|
+
deletedDiffPlacement = _ref7$.deletedDiffPlacement,
|
|
467
|
+
inlineDeletedDiffPlacement = _ref7$.inlineDeletedDiffPlacement;
|
|
535
468
|
var _ref8 = (0, _slicedToArray2.default)(_ref6, 1),
|
|
536
469
|
_ref8$ = _ref8[0],
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
var _ref9 = (0, _slicedToArray2.default)(_ref7, 1),
|
|
551
|
-
_ref9$ = _ref9[0],
|
|
552
|
-
lastPluginState = _ref9$.pluginState,
|
|
553
|
-
lastState = _ref9$.state,
|
|
554
|
-
lastColorScheme = _ref9$.colorScheme,
|
|
555
|
-
lastIntl = _ref9$.intl,
|
|
556
|
-
lastActiveIndexPos = _ref9$.activeIndexPos,
|
|
557
|
-
lastIsInverted = _ref9$.isInverted,
|
|
558
|
-
lastDiffType = _ref9$.diffType,
|
|
559
|
-
lastHideDeletedDiffs = _ref9$.hideDeletedDiffs,
|
|
560
|
-
lastHideAddedDiffsUnderline = _ref9$.hideAddedDiffsUnderline,
|
|
561
|
-
lastShowIndicators = _ref9$.showIndicators,
|
|
562
|
-
lastSmartThresholds = _ref9$.smartThresholds,
|
|
563
|
-
lastDeletedDiffPlacement = _ref9$.deletedDiffPlacement,
|
|
564
|
-
lastInlineDeletedDiffPlacement = _ref9$.inlineDeletedDiffPlacement;
|
|
470
|
+
lastPluginState = _ref8$.pluginState,
|
|
471
|
+
lastState = _ref8$.state,
|
|
472
|
+
lastColorScheme = _ref8$.colorScheme,
|
|
473
|
+
lastIntl = _ref8$.intl,
|
|
474
|
+
lastActiveIndexPos = _ref8$.activeIndexPos,
|
|
475
|
+
lastIsInverted = _ref8$.isInverted,
|
|
476
|
+
lastDiffType = _ref8$.diffType,
|
|
477
|
+
lastHideDeletedDiffs = _ref8$.hideDeletedDiffs,
|
|
478
|
+
lastHideAddedDiffsUnderline = _ref8$.hideAddedDiffsUnderline,
|
|
479
|
+
lastShowIndicators = _ref8$.showIndicators,
|
|
480
|
+
lastSmartThresholds = _ref8$.smartThresholds,
|
|
481
|
+
lastDeletedDiffPlacement = _ref8$.deletedDiffPlacement,
|
|
482
|
+
lastInlineDeletedDiffPlacement = _ref8$.inlineDeletedDiffPlacement;
|
|
565
483
|
var originalDocIsSame = lastPluginState.originalDoc && pluginState.originalDoc && pluginState.originalDoc.eq(lastPluginState.originalDoc);
|
|
566
484
|
if ((0, _isExtendedEnabled.isExtendedEnabled)(diffType)) {
|
|
567
|
-
var
|
|
568
|
-
return (
|
|
485
|
+
var _ref9;
|
|
486
|
+
return (_ref9 = colorScheme === lastColorScheme && intl.locale === lastIntl.locale && isInverted === lastIsInverted && diffType === lastDiffType && (0, _isEqual.default)(activeIndexPos, lastActiveIndexPos) && originalDocIsSame && (0, _isEqual.default)(pluginState.steps, lastPluginState.steps) && state.doc.eq(lastState.doc) && hideDeletedDiffs === lastHideDeletedDiffs && hideAddedDiffsUnderline === lastHideAddedDiffsUnderline && showIndicators === lastShowIndicators && (0, _isEqual.default)(smartThresholds, lastSmartThresholds) && deletedDiffPlacement === lastDeletedDiffPlacement && inlineDeletedDiffPlacement === lastInlineDeletedDiffPlacement) !== null && _ref9 !== void 0 ? _ref9 : false;
|
|
569
487
|
}
|
|
570
|
-
return (
|
|
488
|
+
return (_ref0 = originalDocIsSame && (0, _isEqual.default)(pluginState.steps, lastPluginState.steps) && state.doc.eq(lastState.doc) && colorScheme === lastColorScheme && intl.locale === lastIntl.locale && (0, _isEqual.default)(activeIndexPos, lastActiveIndexPos) && hideDeletedDiffs === lastHideDeletedDiffs) !== null && _ref0 !== void 0 ? _ref0 : false;
|
|
571
489
|
});
|
|
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
|
-
exports.
|
|
7
|
+
exports.diffBySteps = void 0;
|
|
8
8
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
9
|
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
10
10
|
var _prosemirrorChangeset = require("prosemirror-changeset");
|
|
@@ -407,127 +407,4 @@ var diffBySteps = exports.diffBySteps = function diffBySteps(originalDoc, steps)
|
|
|
407
407
|
});
|
|
408
408
|
}
|
|
409
409
|
return mergeOverlappingByNewDocRange(changes);
|
|
410
|
-
};
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* A fork of `diffBySteps` that returns changes grouped per step, rather than as a flat list.
|
|
414
|
-
*
|
|
415
|
-
* Why forked rather than refactoring `diffBySteps`:
|
|
416
|
-
* - `diffBySteps` returns a flat `Change[]` and is consumed by the existing decoration path.
|
|
417
|
-
* Changing its return shape would require threading per-step metadata through all callers,
|
|
418
|
-
* adding complexity to a stable code path.
|
|
419
|
-
* - The per-step grouping is only needed for the `platform_editor_diff_granular_extended` gate,
|
|
420
|
-
* where we need to know how many granular changes a single step produced in order to decide
|
|
421
|
-
* whether to suppress deleted decorations (threshold: > 3 granular changes per step).
|
|
422
|
-
* - Keeping the two functions separate means each has a clear, focused contract and neither
|
|
423
|
-
* accumulates the other's concerns. Shared logic (mapping helpers, `mergeOverlappingByNewDocRange`,
|
|
424
|
-
* `shouldCheckGranularDiff`, etc.) is already extracted and reused by both.
|
|
425
|
-
*/
|
|
426
|
-
var getStepChanges = exports.getStepChanges = function getStepChanges(originalDoc, steps) {
|
|
427
|
-
var result = [];
|
|
428
|
-
var currentDoc = originalDoc;
|
|
429
|
-
var successfulStepMaps = [];
|
|
430
|
-
var rangedSteps = [];
|
|
431
|
-
var _iterator4 = _createForOfIteratorHelper(steps),
|
|
432
|
-
_step4;
|
|
433
|
-
try {
|
|
434
|
-
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
435
|
-
var step = _step4.value;
|
|
436
|
-
var before = currentDoc;
|
|
437
|
-
var stepResult = step.apply(currentDoc);
|
|
438
|
-
if (stepResult.failed !== null || !stepResult.doc) {
|
|
439
|
-
continue;
|
|
440
|
-
}
|
|
441
|
-
var stepMap = step.getMap();
|
|
442
|
-
var rangeStep = step;
|
|
443
|
-
if (typeof rangeStep.from === 'number' && typeof rangeStep.to === 'number') {
|
|
444
|
-
rangedSteps.push({
|
|
445
|
-
before: before,
|
|
446
|
-
doc: stepResult.doc,
|
|
447
|
-
from: rangeStep.from,
|
|
448
|
-
to: rangeStep.to,
|
|
449
|
-
mapIndex: successfulStepMaps.length,
|
|
450
|
-
step: step,
|
|
451
|
-
stepMap: stepMap
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
successfulStepMaps.push(stepMap);
|
|
455
|
-
currentDoc = stepResult.doc;
|
|
456
|
-
}
|
|
457
|
-
} catch (err) {
|
|
458
|
-
_iterator4.e(err);
|
|
459
|
-
} finally {
|
|
460
|
-
_iterator4.f();
|
|
461
|
-
}
|
|
462
|
-
for (var _i2 = 0, _rangedSteps2 = rangedSteps; _i2 < _rangedSteps2.length; _i2++) {
|
|
463
|
-
var rangedStep = _rangedSteps2[_i2];
|
|
464
|
-
var originalToBeforeStep = createMapping(successfulStepMaps.slice(0, rangedStep.mapIndex));
|
|
465
|
-
var beforeStepToOriginal = originalToBeforeStep.invert();
|
|
466
|
-
var fromA = mapPosition(beforeStepToOriginal, rangedStep.from);
|
|
467
|
-
var toA = mapPosition(beforeStepToOriginal, rangedStep.to);
|
|
468
|
-
var fromAfterStep = rangedStep.stepMap.map(rangedStep.from, -1);
|
|
469
|
-
var toAfterStep = rangedStep.stepMap.map(rangedStep.to, 1);
|
|
470
|
-
var afterStepToFinal = createMapping(successfulStepMaps.slice(rangedStep.mapIndex + 1));
|
|
471
|
-
var fromB = mapPosition(afterStepToFinal, fromAfterStep);
|
|
472
|
-
var toB = mapPosition(afterStepToFinal, toAfterStep);
|
|
473
|
-
if (shouldCheckGranularDiff(rangedStep.step, rangedStep.before, rangedStep.from, rangedStep.to)) {
|
|
474
|
-
var granularStepChanges = _prosemirrorChangeset.ChangeSet.create(rangedStep.before).addSteps(rangedStep.doc, [rangedStep.stepMap], null);
|
|
475
|
-
var optimizedGranularStepChanges = (0, _optimizeChanges.optimizeChanges)((0, _prosemirrorChangeset.simplifyChanges)(granularStepChanges.changes, rangedStep.doc));
|
|
476
|
-
var stepChanges = [];
|
|
477
|
-
var _iterator5 = _createForOfIteratorHelper(optimizedGranularStepChanges),
|
|
478
|
-
_step5;
|
|
479
|
-
try {
|
|
480
|
-
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
481
|
-
var granularChange = _step5.value;
|
|
482
|
-
var expandedA = expandToWordBoundaries(rangedStep.before, granularChange.fromA, granularChange.toA);
|
|
483
|
-
var expandedB = expandToWordBoundaries(rangedStep.doc, granularChange.fromB, granularChange.toB);
|
|
484
|
-
var aLeftDelta = granularChange.fromA - expandedA.from;
|
|
485
|
-
var aRightDelta = expandedA.to - granularChange.toA;
|
|
486
|
-
var bLeftDelta = granularChange.fromB - expandedB.from;
|
|
487
|
-
var bRightDelta = expandedB.to - granularChange.toB;
|
|
488
|
-
var finalA = expandedA;
|
|
489
|
-
var finalB = expandedB;
|
|
490
|
-
if (aLeftDelta > bLeftDelta || aRightDelta > bRightDelta) {
|
|
491
|
-
var extraLeft = Math.max(0, aLeftDelta - bLeftDelta);
|
|
492
|
-
var extraRight = Math.max(0, aRightDelta - bRightDelta);
|
|
493
|
-
finalB = expandToWordBoundaries(rangedStep.doc, Math.max(expandedB.from - extraLeft, 0), expandedB.to + extraRight);
|
|
494
|
-
}
|
|
495
|
-
if (bLeftDelta > aLeftDelta || bRightDelta > aRightDelta) {
|
|
496
|
-
var _extraLeft2 = Math.max(0, bLeftDelta - aLeftDelta);
|
|
497
|
-
var _extraRight2 = Math.max(0, bRightDelta - aRightDelta);
|
|
498
|
-
finalA = expandToWordBoundaries(rangedStep.before, Math.max(expandedA.from - _extraLeft2, 0), expandedA.to + _extraRight2);
|
|
499
|
-
}
|
|
500
|
-
stepChanges.push({
|
|
501
|
-
fromA: mapPosition(beforeStepToOriginal, finalA.from),
|
|
502
|
-
toA: mapPosition(beforeStepToOriginal, finalA.to),
|
|
503
|
-
fromB: mapPosition(afterStepToFinal, finalB.from),
|
|
504
|
-
toB: mapPosition(afterStepToFinal, finalB.to),
|
|
505
|
-
deleted: createSpans(Math.max(0, finalA.to - finalA.from)),
|
|
506
|
-
inserted: createSpans(Math.max(0, finalB.to - finalB.from))
|
|
507
|
-
});
|
|
508
|
-
}
|
|
509
|
-
} catch (err) {
|
|
510
|
-
_iterator5.e(err);
|
|
511
|
-
} finally {
|
|
512
|
-
_iterator5.f();
|
|
513
|
-
}
|
|
514
|
-
result.push({
|
|
515
|
-
isGranular: true,
|
|
516
|
-
changes: mergeOverlappingByNewDocRange(stepChanges)
|
|
517
|
-
});
|
|
518
|
-
continue;
|
|
519
|
-
}
|
|
520
|
-
result.push({
|
|
521
|
-
isGranular: false,
|
|
522
|
-
changes: [{
|
|
523
|
-
fromA: fromA,
|
|
524
|
-
toA: toA,
|
|
525
|
-
fromB: fromB,
|
|
526
|
-
toB: toB,
|
|
527
|
-
deleted: createSpans(Math.max(0, toA - fromA)),
|
|
528
|
-
inserted: createSpans(Math.max(0, toB - fromB))
|
|
529
|
-
}]
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
|
-
return result;
|
|
533
410
|
};
|
|
@@ -20,7 +20,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
20
20
|
* that is not in the current document.
|
|
21
21
|
*/
|
|
22
22
|
var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidget = function createNodeChangedDecorationWidget(_ref) {
|
|
23
|
-
var _slice$content, _slice$content2, _slice$content3, _slice$content$firstC;
|
|
23
|
+
var _slice$content, _slice$content2, _slice$content3, _$safeInsertPos$nodeB, _slice$content$firstC;
|
|
24
24
|
var change = _ref.change,
|
|
25
25
|
doc = _ref.doc,
|
|
26
26
|
nodeViewSerializer = _ref.nodeViewSerializer,
|
|
@@ -77,6 +77,14 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
|
|
|
77
77
|
|
|
78
78
|
// For non-table content, use the existing span wrapper approach
|
|
79
79
|
var dom = document.createElement('span');
|
|
80
|
+
var $safeInsertPos = newDoc.resolve(safeInsertPos);
|
|
81
|
+
var isTopLevelInsert = $safeInsertPos.depth === 0;
|
|
82
|
+
var hasPreviousBlock = ((_$safeInsertPos$nodeB = $safeInsertPos.nodeBefore) === null || _$safeInsertPos$nodeB === void 0 ? void 0 : _$safeInsertPos$nodeB.isBlock) === true;
|
|
83
|
+
if ((0, _isExtendedEnabled.isExtendedEnabled)(diffType) && isTopLevelInsert && hasPreviousBlock) {
|
|
84
|
+
// Editor CSS removes the top margin from a block when it is its parent's first child.
|
|
85
|
+
// Keep the serialized block as a subsequent child so each block type retains its own margin.
|
|
86
|
+
dom.append(dom.ownerDocument.createElement('div'));
|
|
87
|
+
}
|
|
80
88
|
// When mediaSingle nodes are rendered inside a widget decoration (e.g. as part of
|
|
81
89
|
// a replaced panel), the centering CSS (margin-left: 50%; transform: translateX(-50%))
|
|
82
90
|
// incorrectly applies because isNestedNode resolves to false (getPos returns 0).
|
|
@@ -10,14 +10,13 @@ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
|
10
10
|
import { areDocsEqualByBlockStructureAndText } from '../areDocsEqualByBlockStructureAndText';
|
|
11
11
|
import { createDocMarginAnchorWidget } from '../decorations/createAnchorDecorationWidgets';
|
|
12
12
|
import { createBlockChangedDecoration } from '../decorations/createBlockChangedDecoration';
|
|
13
|
-
import { createGranularBlockReferenceWidget } from '../decorations/createGranularBlockReferenceWidget';
|
|
14
13
|
import { createInlineChangedDecoration } from '../decorations/createInlineChangedDecoration';
|
|
15
14
|
import { createNodeChangedDecorationWidget } from '../decorations/createNodeChangedDecorationWidget';
|
|
16
15
|
import { extractDiffDescriptors } from '../decorations/decorationKeys';
|
|
17
16
|
import { getAttrChangeRanges, stepIsValidAttrChange } from '../decorations/utils/getAttrChangeRanges';
|
|
18
17
|
import { getMarkChangeRanges } from '../decorations/utils/getMarkChangeRanges';
|
|
19
18
|
import { isExtendedEnabled } from '../isExtendedEnabled';
|
|
20
|
-
import { diffBySteps
|
|
19
|
+
import { diffBySteps } from './diffBySteps';
|
|
21
20
|
import { groupChangesByBlock } from './groupChangesByBlock';
|
|
22
21
|
import { optimizeChanges } from './optimizeChanges';
|
|
23
22
|
import { simplifySteps } from './simplifySteps';
|
|
@@ -235,7 +234,7 @@ const calculateDiffDecorationsInner = ({
|
|
|
235
234
|
|
|
236
235
|
// Our default operations are insertions, so it should match the opposite of isInverted.
|
|
237
236
|
const isInserted = !isInverted;
|
|
238
|
-
const createDecorationsForChange =
|
|
237
|
+
const createDecorationsForChange = change => {
|
|
239
238
|
const isActive = activeIndexPos && change.fromB === activeIndexPos.from && change.toB === activeIndexPos.to;
|
|
240
239
|
|
|
241
240
|
// Hoisted because it decides BOTH where the deleted widget is anchored and — since the
|
|
@@ -247,10 +246,8 @@ const calculateDiffDecorationsInner = ({
|
|
|
247
246
|
inlineDeletedDiffPlacement
|
|
248
247
|
});
|
|
249
248
|
if (change.inserted.length > 0) {
|
|
250
|
-
//
|
|
251
|
-
|
|
252
|
-
// isInverted gates both — on an inverted diff the inserted side is visually the deleted side.
|
|
253
|
-
const shouldHideDeleted = isExtendedEnabled(diffType) ? isInverted && (hideDeletedDiffs || showGranularWithBlock && change.deleted.length > 0) : false;
|
|
249
|
+
// On an inverted diff the inserted side is visually the deleted side.
|
|
250
|
+
const shouldHideDeleted = isExtendedEnabled(diffType) ? isInverted && hideDeletedDiffs : false;
|
|
254
251
|
|
|
255
252
|
// For `smart` NODE-level promotions the change range spans a whole container
|
|
256
253
|
// (e.g. an entire list/table/layout, using outer node bounds). Applying a SINGLE
|
|
@@ -267,7 +264,7 @@ const calculateDiffDecorationsInner = ({
|
|
|
267
264
|
// change. Used to decide if indicator anchor positions should be adjusted
|
|
268
265
|
// inward — when the widget is present the anchor must stay at the block
|
|
269
266
|
// boundary to keep the indicator bar continuous with the deleted content.
|
|
270
|
-
const willRenderDeletedWidget = change.deleted.length > 0 && !(isExtendedEnabled(diffType) && !isInverted &&
|
|
267
|
+
const willRenderDeletedWidget = change.deleted.length > 0 && !(isExtendedEnabled(diffType) && !isInverted && hideDeletedDiffs && change.inserted.length > 0);
|
|
271
268
|
if (isSmartNodeLevel) {
|
|
272
269
|
for (const [from, to] of leafTextblockRanges(tr.doc, change.fromB, change.toB)) {
|
|
273
270
|
decorations.push(...createInlineChangedDecoration({
|
|
@@ -322,7 +319,7 @@ const calculateDiffDecorationsInner = ({
|
|
|
322
319
|
}));
|
|
323
320
|
}
|
|
324
321
|
if (change.deleted.length > 0) {
|
|
325
|
-
const shouldHideDeleted = isExtendedEnabled(diffType) ? !isInverted &&
|
|
322
|
+
const shouldHideDeleted = isExtendedEnabled(diffType) ? !isInverted && hideDeletedDiffs && change.inserted.length > 0 : false;
|
|
326
323
|
if (!shouldHideDeleted) {
|
|
327
324
|
decorations.push(...createNodeChangedDecorationWidget({
|
|
328
325
|
change,
|
|
@@ -343,85 +340,9 @@ const calculateDiffDecorationsInner = ({
|
|
|
343
340
|
}
|
|
344
341
|
}
|
|
345
342
|
};
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
// apply the shouldHideDeleted suppression threshold (> 3 granular changes per step).
|
|
350
|
-
// getChanges returns a flat Change[] with no per-step grouping, making this count impossible
|
|
351
|
-
// to derive after the fact without re-introducing per-change metadata.
|
|
352
|
-
const stepChanges = getStepChanges(originalDoc, steps);
|
|
353
|
-
stepChanges.forEach(({
|
|
354
|
-
isGranular,
|
|
355
|
-
changes: stepChangeList
|
|
356
|
-
}) => {
|
|
357
|
-
const granularCount = isGranular ? stepChangeList.length : 0;
|
|
358
|
-
|
|
359
|
-
// Calculate the average ratio of changed content on both A (original) and B (new)
|
|
360
|
-
// sides of the diff. If 30% or more of the block has changed on average, we show
|
|
361
|
-
// the block reference widget even if the granular change count is below the threshold.
|
|
362
|
-
// Block length is derived from the enclosing text block boundaries rather than the
|
|
363
|
-
// first/last change positions, so unchanged words at the start/end are accounted for.
|
|
364
|
-
let avgChangedRatio = 0;
|
|
365
|
-
if (isGranular && stepChangeList.length > 0) {
|
|
366
|
-
const first = stepChangeList[0];
|
|
367
|
-
const last = stepChangeList[stepChangeList.length - 1];
|
|
368
|
-
const resolvedA = originalDoc.resolve(first.fromA);
|
|
369
|
-
const resolvedB = tr.doc.resolve(first.fromB);
|
|
370
|
-
let blockStartA = first.fromA;
|
|
371
|
-
let blockEndA = last.toA;
|
|
372
|
-
let blockStartB = first.fromB;
|
|
373
|
-
let blockEndB = last.toB;
|
|
374
|
-
for (let depth = resolvedA.depth; depth >= 0; depth--) {
|
|
375
|
-
const node = resolvedA.node(depth);
|
|
376
|
-
if (node.isTextblock) {
|
|
377
|
-
blockStartA = resolvedA.start(depth);
|
|
378
|
-
blockEndA = blockStartA + node.nodeSize - 2; // exclude open/close tokens
|
|
379
|
-
break;
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
for (let depth = resolvedB.depth; depth >= 0; depth--) {
|
|
383
|
-
const node = resolvedB.node(depth);
|
|
384
|
-
if (node.isTextblock) {
|
|
385
|
-
blockStartB = resolvedB.start(depth);
|
|
386
|
-
blockEndB = blockStartB + node.nodeSize - 2; // exclude open/close tokens
|
|
387
|
-
break;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
const blockLengthA = blockEndA - blockStartA;
|
|
391
|
-
const blockLengthB = blockEndB - blockStartB;
|
|
392
|
-
const totalChangedA = stepChangeList.reduce((sum, c) => sum + (c.toA - c.fromA), 0);
|
|
393
|
-
const totalChangedB = stepChangeList.reduce((sum, c) => sum + (c.toB - c.fromB), 0);
|
|
394
|
-
const ratioA = blockLengthA > 0 ? totalChangedA / blockLengthA : 0;
|
|
395
|
-
const ratioB = blockLengthB > 0 ? totalChangedB / blockLengthB : 0;
|
|
396
|
-
avgChangedRatio = (ratioA + ratioB) / 2;
|
|
397
|
-
}
|
|
398
|
-
const showGranularWithBlock = isGranular && granularCount !== 1 && (granularCount > 3 || avgChangedRatio >= 0.3);
|
|
399
|
-
stepChangeList.forEach(change => {
|
|
400
|
-
createDecorationsForChange(change, showGranularWithBlock);
|
|
401
|
-
});
|
|
402
|
-
if (showGranularWithBlock && stepChangeList.length > 0 && !hideDeletedDiffs) {
|
|
403
|
-
const lastChange = stepChangeList[stepChangeList.length - 1];
|
|
404
|
-
const granularBlockDiffId = crypto.randomUUID();
|
|
405
|
-
const blockWidgets = createGranularBlockReferenceWidget({
|
|
406
|
-
change: lastChange,
|
|
407
|
-
originalDoc,
|
|
408
|
-
newDoc: tr.doc,
|
|
409
|
-
isInverted,
|
|
410
|
-
nodeViewSerializer,
|
|
411
|
-
colorScheme,
|
|
412
|
-
intl,
|
|
413
|
-
activeIndexPos,
|
|
414
|
-
diffId: granularBlockDiffId,
|
|
415
|
-
showIndicators
|
|
416
|
-
});
|
|
417
|
-
decorations.push(...blockWidgets);
|
|
418
|
-
}
|
|
419
|
-
});
|
|
420
|
-
} else {
|
|
421
|
-
changes.forEach(change => {
|
|
422
|
-
createDecorationsForChange(change, /* showGranularWithBlock */false);
|
|
423
|
-
});
|
|
424
|
-
}
|
|
343
|
+
changes.forEach(change => {
|
|
344
|
+
createDecorationsForChange(change);
|
|
345
|
+
});
|
|
425
346
|
getMarkChangeRanges(steps).forEach(change => {
|
|
426
347
|
const isActive = activeIndexPos && change.fromB === activeIndexPos.from && change.toB === activeIndexPos.to;
|
|
427
348
|
decorations.push(...createInlineChangedDecoration({
|
|
@@ -362,108 +362,4 @@ export const diffBySteps = (originalDoc, steps) => {
|
|
|
362
362
|
});
|
|
363
363
|
}
|
|
364
364
|
return mergeOverlappingByNewDocRange(changes);
|
|
365
|
-
};
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* A fork of `diffBySteps` that returns changes grouped per step, rather than as a flat list.
|
|
369
|
-
*
|
|
370
|
-
* Why forked rather than refactoring `diffBySteps`:
|
|
371
|
-
* - `diffBySteps` returns a flat `Change[]` and is consumed by the existing decoration path.
|
|
372
|
-
* Changing its return shape would require threading per-step metadata through all callers,
|
|
373
|
-
* adding complexity to a stable code path.
|
|
374
|
-
* - The per-step grouping is only needed for the `platform_editor_diff_granular_extended` gate,
|
|
375
|
-
* where we need to know how many granular changes a single step produced in order to decide
|
|
376
|
-
* whether to suppress deleted decorations (threshold: > 3 granular changes per step).
|
|
377
|
-
* - Keeping the two functions separate means each has a clear, focused contract and neither
|
|
378
|
-
* accumulates the other's concerns. Shared logic (mapping helpers, `mergeOverlappingByNewDocRange`,
|
|
379
|
-
* `shouldCheckGranularDiff`, etc.) is already extracted and reused by both.
|
|
380
|
-
*/
|
|
381
|
-
export const getStepChanges = (originalDoc, steps) => {
|
|
382
|
-
const result = [];
|
|
383
|
-
let currentDoc = originalDoc;
|
|
384
|
-
const successfulStepMaps = [];
|
|
385
|
-
const rangedSteps = [];
|
|
386
|
-
for (const step of steps) {
|
|
387
|
-
const before = currentDoc;
|
|
388
|
-
const stepResult = step.apply(currentDoc);
|
|
389
|
-
if (stepResult.failed !== null || !stepResult.doc) {
|
|
390
|
-
continue;
|
|
391
|
-
}
|
|
392
|
-
const stepMap = step.getMap();
|
|
393
|
-
const rangeStep = step;
|
|
394
|
-
if (typeof rangeStep.from === 'number' && typeof rangeStep.to === 'number') {
|
|
395
|
-
rangedSteps.push({
|
|
396
|
-
before,
|
|
397
|
-
doc: stepResult.doc,
|
|
398
|
-
from: rangeStep.from,
|
|
399
|
-
to: rangeStep.to,
|
|
400
|
-
mapIndex: successfulStepMaps.length,
|
|
401
|
-
step,
|
|
402
|
-
stepMap
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
successfulStepMaps.push(stepMap);
|
|
406
|
-
currentDoc = stepResult.doc;
|
|
407
|
-
}
|
|
408
|
-
for (const rangedStep of rangedSteps) {
|
|
409
|
-
const originalToBeforeStep = createMapping(successfulStepMaps.slice(0, rangedStep.mapIndex));
|
|
410
|
-
const beforeStepToOriginal = originalToBeforeStep.invert();
|
|
411
|
-
const fromA = mapPosition(beforeStepToOriginal, rangedStep.from);
|
|
412
|
-
const toA = mapPosition(beforeStepToOriginal, rangedStep.to);
|
|
413
|
-
const fromAfterStep = rangedStep.stepMap.map(rangedStep.from, -1);
|
|
414
|
-
const toAfterStep = rangedStep.stepMap.map(rangedStep.to, 1);
|
|
415
|
-
const afterStepToFinal = createMapping(successfulStepMaps.slice(rangedStep.mapIndex + 1));
|
|
416
|
-
const fromB = mapPosition(afterStepToFinal, fromAfterStep);
|
|
417
|
-
const toB = mapPosition(afterStepToFinal, toAfterStep);
|
|
418
|
-
if (shouldCheckGranularDiff(rangedStep.step, rangedStep.before, rangedStep.from, rangedStep.to)) {
|
|
419
|
-
const granularStepChanges = ChangeSet.create(rangedStep.before).addSteps(rangedStep.doc, [rangedStep.stepMap], null);
|
|
420
|
-
const optimizedGranularStepChanges = optimizeChanges(simplifyChanges(granularStepChanges.changes, rangedStep.doc));
|
|
421
|
-
const stepChanges = [];
|
|
422
|
-
for (const granularChange of optimizedGranularStepChanges) {
|
|
423
|
-
const expandedA = expandToWordBoundaries(rangedStep.before, granularChange.fromA, granularChange.toA);
|
|
424
|
-
const expandedB = expandToWordBoundaries(rangedStep.doc, granularChange.fromB, granularChange.toB);
|
|
425
|
-
const aLeftDelta = granularChange.fromA - expandedA.from;
|
|
426
|
-
const aRightDelta = expandedA.to - granularChange.toA;
|
|
427
|
-
const bLeftDelta = granularChange.fromB - expandedB.from;
|
|
428
|
-
const bRightDelta = expandedB.to - granularChange.toB;
|
|
429
|
-
let finalA = expandedA;
|
|
430
|
-
let finalB = expandedB;
|
|
431
|
-
if (aLeftDelta > bLeftDelta || aRightDelta > bRightDelta) {
|
|
432
|
-
const extraLeft = Math.max(0, aLeftDelta - bLeftDelta);
|
|
433
|
-
const extraRight = Math.max(0, aRightDelta - bRightDelta);
|
|
434
|
-
finalB = expandToWordBoundaries(rangedStep.doc, Math.max(expandedB.from - extraLeft, 0), expandedB.to + extraRight);
|
|
435
|
-
}
|
|
436
|
-
if (bLeftDelta > aLeftDelta || bRightDelta > aRightDelta) {
|
|
437
|
-
const extraLeft = Math.max(0, bLeftDelta - aLeftDelta);
|
|
438
|
-
const extraRight = Math.max(0, bRightDelta - aRightDelta);
|
|
439
|
-
finalA = expandToWordBoundaries(rangedStep.before, Math.max(expandedA.from - extraLeft, 0), expandedA.to + extraRight);
|
|
440
|
-
}
|
|
441
|
-
stepChanges.push({
|
|
442
|
-
fromA: mapPosition(beforeStepToOriginal, finalA.from),
|
|
443
|
-
toA: mapPosition(beforeStepToOriginal, finalA.to),
|
|
444
|
-
fromB: mapPosition(afterStepToFinal, finalB.from),
|
|
445
|
-
toB: mapPosition(afterStepToFinal, finalB.to),
|
|
446
|
-
deleted: createSpans(Math.max(0, finalA.to - finalA.from)),
|
|
447
|
-
inserted: createSpans(Math.max(0, finalB.to - finalB.from))
|
|
448
|
-
});
|
|
449
|
-
}
|
|
450
|
-
result.push({
|
|
451
|
-
isGranular: true,
|
|
452
|
-
changes: mergeOverlappingByNewDocRange(stepChanges)
|
|
453
|
-
});
|
|
454
|
-
continue;
|
|
455
|
-
}
|
|
456
|
-
result.push({
|
|
457
|
-
isGranular: false,
|
|
458
|
-
changes: [{
|
|
459
|
-
fromA,
|
|
460
|
-
toA,
|
|
461
|
-
fromB,
|
|
462
|
-
toB,
|
|
463
|
-
deleted: createSpans(Math.max(0, toA - fromA)),
|
|
464
|
-
inserted: createSpans(Math.max(0, toB - fromB))
|
|
465
|
-
}]
|
|
466
|
-
});
|
|
467
|
-
}
|
|
468
|
-
return result;
|
|
469
365
|
};
|