@atlaskit/editor-plugin-show-diff
Version:
ShowDiff plugin for @atlaskit/editor-core
242 lines (240 loc) • 13.1 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.calculateDiffDecorations = void 0;
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _isEqual = _interopRequireDefault(require("lodash/isEqual"));
var _memoizeOne = _interopRequireDefault(require("memoize-one"));
var _prosemirrorChangeset = require("prosemirror-changeset");
var _document = require("@atlaskit/editor-common/utils/document");
var _view = require("@atlaskit/editor-prosemirror/view");
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
var _areDocsEqualByBlockStructureAndText = require("../areDocsEqualByBlockStructureAndText");
var _createBlockChangedDecoration = require("../decorations/createBlockChangedDecoration");
var _createInlineChangedDecoration = require("../decorations/createInlineChangedDecoration");
var _createNodeChangedDecorationWidget = require("../decorations/createNodeChangedDecorationWidget");
var _getAttrChangeRanges = require("../decorations/utils/getAttrChangeRanges");
var _getMarkChangeRanges = require("../decorations/utils/getMarkChangeRanges");
var _groupChangesByBlock = require("./groupChangesByBlock");
var _optimizeChanges = require("./optimizeChanges");
var _simplifySteps = require("./simplifySteps");
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) { (0, _defineProperty2.default)(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
var getChanges = function getChanges(_ref) {
var changeset = _ref.changeset,
originalDoc = _ref.originalDoc,
steppedDoc = _ref.steppedDoc,
diffType = _ref.diffType,
tr = _ref.tr;
if (diffType === 'block' && (0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
return (0, _groupChangesByBlock.groupChangesByBlock)(changeset.changes, originalDoc, steppedDoc);
}
var changes = (0, _prosemirrorChangeset.simplifyChanges)(changeset.changes, tr.doc);
return (0, _optimizeChanges.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;
var decorations = [];
// Iterate over the document nodes within the range
doc.nodesBetween(from, to, function (node, pos) {
if (node.isBlock && (!(0, _expValEquals.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 decoration = (0, _createBlockChangedDecoration.createBlockChangedDecoration)({
change: {
from: pos,
to: nodeEnd,
name: node.type.name
},
colorScheme: colorScheme,
isInserted: isInserted,
isActive: isActive
});
if (decoration) {
decorations.push(decoration);
}
}
});
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;
var originalDoc = pluginState.originalDoc,
steps = pluginState.steps;
if (!originalDoc || !pluginState.isDisplayingChanges) {
return _view.DecorationSet.empty;
}
var tr = state.tr;
var steppedDoc = originalDoc;
var attrSteps = [];
var simplifiedSteps = (0, _simplifySteps.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 ((0, _getAttrChangeRanges.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 (!(0, _document.areNodesEqualIgnoreAttrs)(steppedDoc, tr.doc)) {
var recoveredViaContentEquality = (0, _platformFeatureFlags.fg)('platform_editor_show_diff_equality_fallback') ? (0, _areDocsEqualByBlockStructureAndText.areDocsEqualByBlockStructureAndText)(steppedDoc, tr.doc) : undefined;
if ((0, _expValEquals.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 ((0, _platformFeatureFlags.fg)('platform_editor_show_diff_equality_fallback')) {
if (!recoveredViaContentEquality) {
return _view.DecorationSet.empty;
}
} else {
return _view.DecorationSet.empty;
}
}
var changeset = _prosemirrorChangeset.ChangeSet.create(originalDoc).addSteps(steppedDoc, stepMaps, tr.doc);
var changes = getChanges({
changeset: changeset,
originalDoc: originalDoc,
steppedDoc: steppedDoc,
diffType: diffType,
tr: tr
});
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) {
decorations.push((0, _createInlineChangedDecoration.createInlineChangedDecoration)(_objectSpread({
change: change,
colorScheme: colorScheme,
isActive: isActive
}, (0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true) && {
isInserted: isInserted
})));
decorations.push.apply(decorations, (0, _toConsumableArray2.default)(calculateNodesForBlockDecoration(_objectSpread(_objectSpread({
doc: tr.doc,
from: change.fromB,
to: change.toB,
colorScheme: colorScheme
}, (0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true) && {
isInserted: isInserted
}), {}, {
activeIndexPos: activeIndexPos,
intl: intl
}))));
}
if (change.deleted.length > 0) {
var decoration = (0, _createNodeChangedDecorationWidget.createNodeChangedDecorationWidget)(_objectSpread({
change: change,
doc: originalDoc,
nodeViewSerializer: nodeViewSerializer,
colorScheme: colorScheme,
newDoc: tr.doc,
intl: intl,
activeIndexPos: activeIndexPos
}, (0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true) && {
isInserted: !isInserted
}));
if (decoration) {
decorations.push.apply(decorations, (0, _toConsumableArray2.default)(decoration));
}
}
});
(0, _getMarkChangeRanges.getMarkChangeRanges)(steps).forEach(function (change) {
var isActive = activeIndexPos && change.fromB === activeIndexPos.from && change.toB === activeIndexPos.to;
decorations.push((0, _createInlineChangedDecoration.createInlineChangedDecoration)({
change: change,
colorScheme: colorScheme,
isActive: isActive,
isInserted: true
}));
});
(0, _getAttrChangeRanges.getAttrChangeRanges)(tr.doc, attrSteps).forEach(function (change) {
decorations.push.apply(decorations, (0, _toConsumableArray2.default)(calculateNodesForBlockDecoration({
doc: tr.doc,
from: change.fromB,
to: change.toB,
colorScheme: colorScheme,
isInserted: true,
activeIndexPos: activeIndexPos,
intl: intl
})));
});
return _view.DecorationSet.empty.add(tr.doc, decorations);
};
var calculateDiffDecorations = exports.calculateDiffDecorations = (0, _memoizeOne.default)(calculateDiffDecorationsInner,
// Cache results unless relevant inputs change
function (_ref4, _ref5) {
var _ref9;
var _ref6 = (0, _slicedToArray2.default)(_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;
var _ref7 = (0, _slicedToArray2.default)(_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;
var originalDocIsSame = lastPluginState.originalDoc && pluginState.originalDoc && pluginState.originalDoc.eq(lastPluginState.originalDoc);
if ((0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
var _ref8;
return (_ref8 = 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)) !== null && _ref8 !== void 0 ? _ref8 : false;
}
return (_ref9 = originalDocIsSame && (0, _isEqual.default)(pluginState.steps, lastPluginState.steps) && state.doc.eq(lastState.doc) && colorScheme === lastColorScheme && intl.locale === lastIntl.locale && (0, _isEqual.default)(activeIndexPos, lastActiveIndexPos)) !== null && _ref9 !== void 0 ? _ref9 : false;
});