@atlaskit/editor-plugin-show-diff
Version:
ShowDiff plugin for @atlaskit/editor-core
260 lines (259 loc) • 13.4 kB
JavaScript
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
// eslint-disable-next-line @atlassian/tangerine/import/entry-points
import isEqual from 'lodash/isEqual';
import memoizeOne from 'memoize-one';
import { ChangeSet, simplifyChanges } from 'prosemirror-changeset';
import { areNodesEqualIgnoreAttrs } from '@atlaskit/editor-common/utils/document';
import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
import { fg } from '@atlaskit/platform-feature-flags';
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
import { areDocsEqualByBlockStructureAndText } from '../areDocsEqualByBlockStructureAndText';
import { createBlockChangedDecoration } from '../decorations/createBlockChangedDecoration';
import { createInlineChangedDecoration } from '../decorations/createInlineChangedDecoration';
import { createNodeChangedDecorationWidget } from '../decorations/createNodeChangedDecorationWidget';
import { getAttrChangeRanges, stepIsValidAttrChange } from '../decorations/utils/getAttrChangeRanges';
import { getMarkChangeRanges } from '../decorations/utils/getMarkChangeRanges';
import { diffBySteps } from './diffBySteps';
import { groupChangesByBlock } from './groupChangesByBlock';
import { optimizeChanges } from './optimizeChanges';
import { simplifySteps } from './simplifySteps';
var getChanges = function getChanges(_ref) {
var changeset = _ref.changeset,
originalDoc = _ref.originalDoc,
steppedDoc = _ref.steppedDoc,
diffType = _ref.diffType,
tr = _ref.tr,
steps = _ref.steps;
if (expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
if (diffType === 'step') {
return diffBySteps(originalDoc, steps);
}
if (diffType === 'block') {
return groupChangesByBlock(changeset.changes, originalDoc, steppedDoc);
}
var _changes = simplifyChanges(changeset.changes, tr.doc);
return optimizeChanges(_changes);
}
var changes = simplifyChanges(changeset.changes, tr.doc);
return optimizeChanges(changes);
};
var calculateNodesForBlockDecoration = function calculateNodesForBlockDecoration(_ref2) {
var doc = _ref2.doc,
from = _ref2.from,
to = _ref2.to,
colorScheme = _ref2.colorScheme,
_ref2$isInserted = _ref2.isInserted,
isInserted = _ref2$isInserted === void 0 ? true : _ref2$isInserted,
activeIndexPos = _ref2.activeIndexPos,
_ref2$shouldHideDelet = _ref2.shouldHideDeleted,
shouldHideDeleted = _ref2$shouldHideDelet === void 0 ? false : _ref2$shouldHideDelet;
var decorations = [];
// Iterate over the document nodes within the range
doc.nodesBetween(from, to, function (node, pos) {
if (node.isBlock && (!expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) || pos + node.nodeSize <= to)) {
var nodeEnd = pos + node.nodeSize;
var isActive = activeIndexPos && pos === activeIndexPos.from && nodeEnd === activeIndexPos.to;
var blockChangedDecorations = createBlockChangedDecoration({
change: {
from: pos,
to: nodeEnd,
name: node.type.name
},
colorScheme: colorScheme,
isInserted: isInserted,
isActive: isActive,
shouldHideDeleted: shouldHideDeleted
});
if (blockChangedDecorations.length) {
decorations.push.apply(decorations, _toConsumableArray(blockChangedDecorations));
}
}
});
return decorations;
};
var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref3) {
var state = _ref3.state,
pluginState = _ref3.pluginState,
nodeViewSerializer = _ref3.nodeViewSerializer,
colorScheme = _ref3.colorScheme,
intl = _ref3.intl,
activeIndexPos = _ref3.activeIndexPos,
api = _ref3.api,
_ref3$isInverted = _ref3.isInverted,
isInverted = _ref3$isInverted === void 0 ? false : _ref3$isInverted,
_ref3$diffType = _ref3.diffType,
diffType = _ref3$diffType === void 0 ? 'inline' : _ref3$diffType,
_ref3$hideDeletedDiff = _ref3.hideDeletedDiffs,
hideDeletedDiffs = _ref3$hideDeletedDiff === void 0 ? false : _ref3$hideDeletedDiff;
var originalDoc = pluginState.originalDoc,
steps = pluginState.steps;
if (!originalDoc || !pluginState.isDisplayingChanges) {
return DecorationSet.empty;
}
var tr = state.tr;
var steppedDoc = originalDoc;
var attrSteps = [];
var simplifiedSteps = simplifySteps(steps, originalDoc);
var stepMaps = [];
var _iterator = _createForOfIteratorHelper(simplifiedSteps),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var step = _step.value;
var result = step.apply(steppedDoc);
if (result.failed === null && result.doc) {
if (stepIsValidAttrChange(step, steppedDoc, result.doc)) {
attrSteps.push(step);
}
stepMaps.push(step.getMap());
steppedDoc = result.doc;
}
}
// Rather than using .eq() we use a custom function that only checks for structural
// changes and ignores differences in attributes which don't affect decoration positions
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
if (!areNodesEqualIgnoreAttrs(steppedDoc, tr.doc)) {
var recoveredViaContentEquality = fg('platform_editor_show_diff_equality_fallback') ? areDocsEqualByBlockStructureAndText(steppedDoc, tr.doc) : undefined;
if (expValEquals('platform_editor_are_nodes_equal_ignore_mark_order', 'isEnabled', true)) {
var _api$analytics;
api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || _api$analytics.actions.fireAnalyticsEvent({
eventType: 'track',
action: 'nodesNotEqual',
actionSubject: 'showDiff',
attributes: {
docSizeEqual: steppedDoc.nodeSize === tr.doc.nodeSize,
colorScheme: colorScheme,
recoveredViaContentEquality: recoveredViaContentEquality
}
});
}
if (fg('platform_editor_show_diff_equality_fallback')) {
if (!recoveredViaContentEquality) {
return DecorationSet.empty;
}
} else {
return DecorationSet.empty;
}
}
var changeset = ChangeSet.create(originalDoc).addSteps(steppedDoc, stepMaps, tr.doc);
var changes = getChanges({
changeset: changeset,
originalDoc: originalDoc,
steppedDoc: steppedDoc,
diffType: diffType,
tr: tr,
steps: steps
});
var decorations = [];
changes.forEach(function (change) {
var isActive = activeIndexPos && change.fromB === activeIndexPos.from && change.toB === activeIndexPos.to;
// Our default operations are insertions, so it should match the opposite of isInverted.
var isInserted = !isInverted;
if (change.inserted.length > 0) {
var shouldHideDeleted = expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? isInverted && hideDeletedDiffs : false;
decorations.push(createInlineChangedDecoration(_objectSpread({
change: change,
colorScheme: colorScheme,
isActive: isActive
}, expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) && {
isInserted: isInserted,
shouldHideDeleted: shouldHideDeleted
})));
decorations.push.apply(decorations, _toConsumableArray(calculateNodesForBlockDecoration(_objectSpread(_objectSpread({
doc: tr.doc,
from: change.fromB,
to: change.toB,
colorScheme: colorScheme
}, expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) && {
isInserted: isInserted,
shouldHideDeleted: shouldHideDeleted
}), {}, {
activeIndexPos: activeIndexPos,
intl: intl
}))));
}
if (change.deleted.length > 0) {
var _shouldHideDeleted = expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? !isInverted && hideDeletedDiffs : false;
if (!_shouldHideDeleted) {
var decoration = createNodeChangedDecorationWidget(_objectSpread({
change: change,
doc: originalDoc,
nodeViewSerializer: nodeViewSerializer,
colorScheme: colorScheme,
newDoc: tr.doc,
intl: intl,
activeIndexPos: activeIndexPos
}, expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) && {
isInserted: !isInserted,
diffType: diffType
}));
if (decoration) {
decorations.push.apply(decorations, _toConsumableArray(decoration));
}
}
}
});
getMarkChangeRanges(steps).forEach(function (change) {
var isActive = activeIndexPos && change.fromB === activeIndexPos.from && change.toB === activeIndexPos.to;
decorations.push(createInlineChangedDecoration({
change: change,
colorScheme: colorScheme,
isActive: isActive,
isInserted: true
}));
});
getAttrChangeRanges(tr.doc, attrSteps).forEach(function (change) {
decorations.push.apply(decorations, _toConsumableArray(calculateNodesForBlockDecoration({
doc: tr.doc,
from: change.fromB,
to: change.toB,
colorScheme: colorScheme,
isInserted: true,
activeIndexPos: activeIndexPos,
intl: intl
})));
});
return DecorationSet.empty.add(tr.doc, decorations);
};
export var calculateDiffDecorations = memoizeOne(calculateDiffDecorationsInner,
// Cache results unless relevant inputs change
function (_ref4, _ref5) {
var _ref9;
var _ref6 = _slicedToArray(_ref4, 1),
_ref6$ = _ref6[0],
pluginState = _ref6$.pluginState,
state = _ref6$.state,
colorScheme = _ref6$.colorScheme,
intl = _ref6$.intl,
activeIndexPos = _ref6$.activeIndexPos,
isInverted = _ref6$.isInverted,
diffType = _ref6$.diffType,
hideDeletedDiffs = _ref6$.hideDeletedDiffs;
var _ref7 = _slicedToArray(_ref5, 1),
_ref7$ = _ref7[0],
lastPluginState = _ref7$.pluginState,
lastState = _ref7$.state,
lastColorScheme = _ref7$.colorScheme,
lastIntl = _ref7$.intl,
lastActiveIndexPos = _ref7$.activeIndexPos,
lastIsInverted = _ref7$.isInverted,
lastDiffType = _ref7$.diffType,
lastHideDeletedDiffs = _ref7$.hideDeletedDiffs;
var originalDocIsSame = lastPluginState.originalDoc && pluginState.originalDoc && pluginState.originalDoc.eq(lastPluginState.originalDoc);
if (expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
var _ref8;
return (_ref8 = colorScheme === lastColorScheme && intl.locale === lastIntl.locale && isInverted === lastIsInverted && diffType === lastDiffType && isEqual(activeIndexPos, lastActiveIndexPos) && originalDocIsSame && isEqual(pluginState.steps, lastPluginState.steps) && state.doc.eq(lastState.doc) && hideDeletedDiffs === lastHideDeletedDiffs) !== null && _ref8 !== void 0 ? _ref8 : false;
}
return (_ref9 = originalDocIsSame && isEqual(pluginState.steps, lastPluginState.steps) && state.doc.eq(lastState.doc) && colorScheme === lastColorScheme && intl.locale === lastIntl.locale && isEqual(activeIndexPos, lastActiveIndexPos) && hideDeletedDiffs === lastHideDeletedDiffs) !== null && _ref9 !== void 0 ? _ref9 : false;
});