@atlaskit/editor-plugin-show-diff
Version:
ShowDiff plugin for @atlaskit/editor-core
552 lines (543 loc) • 27.6 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
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; }
import isEqual from 'lodash/isEqual';
import memoizeOne from 'memoize-one';
import { ChangeSet, simplifyChanges } from 'prosemirror-changeset';
import { isExperimentEnabled } from '@atlaskit/editor-common/deprecated-platform-feature-experiments';
import { areNodesEqualIgnoreAttrs } from '@atlaskit/editor-common/utils/document';
import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
import { fg } from '@atlaskit/platform-feature-flags/fg';
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
import { areDocsEqualByBlockStructureAndText } from '../areDocsEqualByBlockStructureAndText';
import { createDocMarginAnchorWidget } from '../decorations/createAnchorDecorationWidgets';
import { createBlockChangedDecoration } from '../decorations/createBlockChangedDecoration';
import { createInlineChangedDecoration } from '../decorations/createInlineChangedDecoration';
import { createNodeChangedDecorationWidget } from '../decorations/createNodeChangedDecorationWidget';
import { extractDiffDescriptors } from '../decorations/decorationKeys';
import { getAttrChangeRanges, stepIsValidAttrChange } from '../decorations/utils/getAttrChangeRanges';
import { getMarkChangeRanges } from '../decorations/utils/getMarkChangeRanges';
import { getDefaultDiffType, isExtendedEnabled } from '../isExtendedEnabled';
import { attrAwareTokenEncoder } from './attrAwareTokenEncoder';
import { diffBySteps } from './diffBySteps';
import { groupChangesByBlock } from './groupChangesByBlock';
import { optimizeChanges } from './optimizeChanges';
import { simplifySteps } from './simplifySteps';
import { classifySmartChanges } from './smart/classifySmartChanges';
import { smartChangeLevel } from './smart/helpers';
var getChanges = function getChanges(_ref) {
var changeset = _ref.changeset,
originalDoc = _ref.originalDoc,
steppedDoc = _ref.steppedDoc,
diffType = _ref.diffType,
tr = _ref.tr,
steps = _ref.steps,
intl = _ref.intl,
smartThresholds = _ref.smartThresholds;
if (isExtendedEnabled(diffType)) {
// The `smart` diff type is gated behind `platform_editor_ai_smart_diff`. When the gate is
// off, `smart` falls through to the default (`inline`) path below so behaviour degrades
// gracefully (see docs/smart-diff-design.md §3).
if (diffType === 'smart' && fg('platform_editor_ai_smart_diff')) {
var _changes = simplifyChanges(changeset.changes, tr.doc);
return classifySmartChanges({
changes: _changes,
originalDoc: originalDoc,
newDoc: tr.doc,
locale: intl.locale,
thresholds: smartThresholds
});
}
if (diffType === 'step') {
return diffBySteps(originalDoc, steps);
}
if (diffType === 'block') {
return groupChangesByBlock(changeset.changes, originalDoc, steppedDoc);
}
var _changes2 = simplifyChanges(changeset.changes, tr.doc);
return optimizeChanges(_changes2);
}
var changes = simplifyChanges(changeset.changes, tr.doc);
return optimizeChanges(changes);
};
/**
* Collect the inline-content ranges of every leaf text-bearing block (paragraph, heading, …)
* whose content overlaps `[from, to)`. Used to clip a node-level `smart` insertion highlight to
* the actual added text, so the inserted background/underline never spans structural gaps
* (list markers, empty item slots, cell/column boundaries) which would render as phantom rows.
*/
var leafTextblockRanges = function leafTextblockRanges(doc, from, to) {
var ranges = [];
doc.nodesBetween(from, to, function (node, pos) {
if (node.isTextblock && node.content.size > 0) {
var contentFrom = Math.max(pos + 1, from);
var contentTo = Math.min(pos + 1 + node.content.size, to);
if (contentTo > contentFrom) {
ranges.push([contentFrom, contentTo]);
}
// Textblocks have no block children to descend into.
return false;
}
return true;
});
return ranges;
};
// A large inserted table is treated with a coarse decoration path (see below)
// once it has more leaf textblocks (~cells) than this threshold. Small tables
// keep the precise per-cell path.
var LARGE_TABLE_LEAF_THRESHOLD = 40;
var isLargeInsertedTableRange = function isLargeInsertedTableRange(doc, from, to) {
var containsTable = false;
var leafCount = 0;
doc.nodesBetween(from, to, function (node) {
if (node.type.name === 'table') {
containsTable = true;
}
if (node.isTextblock) {
leafCount += 1;
return false;
}
return true;
});
return containsTable && leafCount > LARGE_TABLE_LEAF_THRESHOLD;
};
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,
_ref2$showIndicators = _ref2.showIndicators,
showIndicators = _ref2$showIndicators === void 0 ? false : _ref2$showIndicators,
diffType = _ref2.diffType,
_ref2$coarseTableCell = _ref2.coarseTableCellsOnly,
coarseTableCellsOnly = _ref2$coarseTableCell === void 0 ? false : _ref2$coarseTableCell;
var decorations = [];
// Iterate over the document nodes within the range
doc.nodesBetween(from, to, function (node, pos) {
// Coarse path: only cell/header overlays are visible; table/row/paragraph
// decorations are redundant and cause expensive per-cell re-render.
if (coarseTableCellsOnly) {
var name = node.type.name;
if (name !== 'tableCell' && name !== 'tableHeader') {
return;
}
}
if (node.isBlock && (!isExtendedEnabled(diffType) || pos + node.nodeSize <= to)) {
var nodeEnd = pos + node.nodeSize;
var isActive = activeIndexPos && pos === activeIndexPos.from && nodeEnd === activeIndexPos.to;
decorations.push.apply(decorations, _toConsumableArray(createBlockChangedDecoration({
change: {
from: pos,
to: nodeEnd,
name: node.type.name
},
colorScheme: colorScheme,
isInserted: isInserted,
isActive: isActive,
shouldHideDeleted: shouldHideDeleted,
showIndicators: showIndicators,
doc: doc,
diffType: diffType
})));
}
});
return decorations;
};
/**
* Whether deleted content for `change` renders after the new content instead of before it.
*
* show-diff splits this across two options by change granularity, so the level decides which one
* applies:
* - node/paragraph-level: `deletedDiffPlacement` (default `'top'`). Pure deletions are promoted to
* node level, so they are governed here too.
* - inline/sentence-level: `inlineDeletedDiffPlacement` (default `'before'`).
*
* Callers are responsible for the `smart` diffType and gate checks.
*/
export var isDeletedContentPlacedBelow = function isDeletedContentPlacedBelow(_ref3) {
var change = _ref3.change,
deletedDiffPlacement = _ref3.deletedDiffPlacement,
inlineDeletedDiffPlacement = _ref3.inlineDeletedDiffPlacement;
var level = smartChangeLevel(change);
if (level === 'node' || level === 'paragraph') {
return deletedDiffPlacement === 'bottom';
}
// Inline-level (undefined) and sentence-level changes.
return inlineDeletedDiffPlacement === 'after';
};
var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4) {
var state = _ref4.state,
pluginState = _ref4.pluginState,
nodeViewSerializer = _ref4.nodeViewSerializer,
colorScheme = _ref4.colorScheme,
intl = _ref4.intl,
activeIndexPos = _ref4.activeIndexPos,
api = _ref4.api,
_ref4$isInverted = _ref4.isInverted,
isInverted = _ref4$isInverted === void 0 ? false : _ref4$isInverted,
_ref4$diffType = _ref4.diffType,
diffType = _ref4$diffType === void 0 ? getDefaultDiffType() : _ref4$diffType,
_ref4$hideDeletedDiff = _ref4.hideDeletedDiffs,
hideDeletedDiffs = _ref4$hideDeletedDiff === void 0 ? false : _ref4$hideDeletedDiff,
_ref4$hideAddedDiffsU = _ref4.hideAddedDiffsUnderline,
hideAddedDiffsUnderlineParam = _ref4$hideAddedDiffsU === void 0 ? false : _ref4$hideAddedDiffsU,
_ref4$showIndicators = _ref4.showIndicators,
showIndicators = _ref4$showIndicators === void 0 ? false : _ref4$showIndicators,
smartThresholds = _ref4.smartThresholds,
_ref4$deletedDiffPlac = _ref4.deletedDiffPlacement,
deletedDiffPlacement = _ref4$deletedDiffPlac === void 0 ? 'top' : _ref4$deletedDiffPlac,
_ref4$inlineDeletedDi = _ref4.inlineDeletedDiffPlacement,
inlineDeletedDiffPlacement = _ref4$inlineDeletedDi === void 0 ? 'before' : _ref4$inlineDeletedDi;
var originalDoc = pluginState.originalDoc,
steps = pluginState.steps,
isDisplayingChanges = pluginState.isDisplayingChanges;
if (!originalDoc || !isDisplayingChanges) {
return {
decorations: DecorationSet.empty,
diffDescriptors: []
};
}
// Resolve the option against its gate once here, so every downstream inline/block builder
// receives the same value. When the gate is off the option is a no-op.
var hideAddedDiffsUnderline = hideAddedDiffsUnderlineParam && fg('platform_editor_ai_smart_diff');
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 _api$analytics;
var recoveredViaContentEquality = areDocsEqualByBlockStructureAndText(steppedDoc, tr.doc);
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 (!recoveredViaContentEquality) {
return {
decorations: DecorationSet.empty,
diffDescriptors: []
};
}
}
// The attribute-aware encoder is only needed by the smart classifier and is
// gated with it; other diff types keep the library default so their output is
// unchanged.
var tokenEncoder = diffType === 'smart' && fg('platform_editor_ai_smart_diff') ? attrAwareTokenEncoder : undefined;
var changeset = ChangeSet.create(originalDoc, undefined, tokenEncoder).addSteps(steppedDoc, stepMaps, tr.doc);
var changes = getChanges({
changeset: changeset,
originalDoc: originalDoc,
steppedDoc: steppedDoc,
diffType: diffType,
tr: tr,
steps: steps,
intl: intl,
smartThresholds: smartThresholds
});
var decorations = [];
/**
* If showIndicators is on, we create an anchor widget here to mark the doc margin.
*/
if (showIndicators && isExtendedEnabled(diffType)) {
decorations.push(createDocMarginAnchorWidget());
}
// Our default operations are insertions, so it should match the opposite of isInverted.
var isInserted = !isInverted;
var createDecorationsForChange = function createDecorationsForChange(change) {
var isActive = activeIndexPos && change.fromB === activeIndexPos.from && change.toB === activeIndexPos.to;
// Hoisted because it decides BOTH where the deleted widget is anchored and — since the
// widget pins whichever end of the range it sits at — how the indicator anchors below are
// allowed to move.
var isDeletedWidgetBelow = diffType === 'smart' && fg('platform_editor_ai_smart_diff') && isDeletedContentPlacedBelow({
change: change,
deletedDiffPlacement: deletedDiffPlacement,
inlineDeletedDiffPlacement: inlineDeletedDiffPlacement
});
if (change.inserted.length > 0) {
// On an inverted diff the inserted side is visually the deleted side.
var shouldHideDeleted = isExtendedEnabled(diffType) ? isInverted && hideDeletedDiffs : false;
// For `smart` NODE-level promotions the change range spans a whole container
// (e.g. an entire list/table/layout, using outer node bounds). Applying a SINGLE
// inline decoration across that whole range would paint the inserted style across
// block boundaries and structural gaps (list markers, empty item slots), producing
// phantom "empty" rows above the real content. But skipping the inline highlight
// entirely leaves added text-bearing blocks (paragraphs/headings inside the added
// container) without the inserted background+underline, because block decorations
// return no style for paragraph/heading. So for node-level smart changes we instead
// emit ONE inline decoration per leaf text-bearing block within the range — the text
// gets highlighted, and structural gaps never do.
var isSmartNodeLevel = diffType === 'smart' && fg('platform_editor_ai_smart_diff') && smartChangeLevel(change) === 'node';
// For a large inserted table, skip the per-cell inline decorations that
// freeze the browser on re-render. Only active in the AIFC experience.
var useCoarseTableDecoration = isSmartNodeLevel && expValEquals('platform_editor_ai_new_aifc_editor_experience', 'isBackendReviewMomentEnabled', true) && isLargeInsertedTableRange(tr.doc, change.fromB, change.toB);
// Whether the deleted-content widget will actually be rendered for this
// change. Used to decide if indicator anchor positions should be adjusted
// inward — when the widget is present the anchor must stay at the block
// boundary to keep the indicator bar continuous with the deleted content.
var willRenderDeletedWidget = change.deleted.length > 0 && !(isExtendedEnabled(diffType) && !isInverted && hideDeletedDiffs && change.inserted.length > 0);
if (useCoarseTableDecoration) {
// Skip the O(cells) inline decorations; the cell overlays (added below)
// provide the highlight without the expensive content re-render.
} else if (isSmartNodeLevel) {
var _iterator2 = _createForOfIteratorHelper(leafTextblockRanges(tr.doc, change.fromB, change.toB)),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var _step2$value = _slicedToArray(_step2.value, 2),
from = _step2$value[0],
to = _step2$value[1];
decorations.push.apply(decorations, _toConsumableArray(createInlineChangedDecoration(_objectSpread({
change: {
fromB: from,
toB: to
},
doc: tr.doc,
colorScheme: colorScheme,
isActive: isActive,
diffType: diffType
}, isExtendedEnabled(diffType) && {
isInserted: isInserted,
shouldHideDeleted: shouldHideDeleted,
showIndicators: showIndicators,
hideAddedDiffsUnderline: hideAddedDiffsUnderline,
hasDeletedWidget: willRenderDeletedWidget,
isDeletedWidgetBelow: isDeletedWidgetBelow
}))));
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
} else {
decorations.push.apply(decorations, _toConsumableArray(createInlineChangedDecoration(_objectSpread({
change: change,
doc: tr.doc,
colorScheme: colorScheme,
isActive: isActive,
diffType: diffType
}, isExtendedEnabled(diffType) && {
isInserted: isInserted,
shouldHideDeleted: shouldHideDeleted,
showIndicators: showIndicators,
hideAddedDiffsUnderline: hideAddedDiffsUnderline,
hasDeletedWidget: willRenderDeletedWidget,
isDeletedWidgetBelow: isDeletedWidgetBelow
}))));
}
decorations.push.apply(decorations, _toConsumableArray(calculateNodesForBlockDecoration(_objectSpread(_objectSpread({
doc: tr.doc,
from: change.fromB,
to: change.toB,
colorScheme: colorScheme
}, isExtendedEnabled(diffType) && {
isInserted: isInserted,
shouldHideDeleted: shouldHideDeleted,
showIndicators: showIndicators
}), {}, {
activeIndexPos: activeIndexPos,
intl: intl,
diffType: diffType,
coarseTableCellsOnly: useCoarseTableDecoration
}))));
}
if (change.deleted.length > 0) {
var _shouldHideDeleted = isExtendedEnabled(diffType) ? !isInverted && hideDeletedDiffs && change.inserted.length > 0 : false;
if (!_shouldHideDeleted) {
decorations.push.apply(decorations, _toConsumableArray(createNodeChangedDecorationWidget(_objectSpread(_objectSpread({
change: change,
doc: originalDoc,
nodeViewSerializer: nodeViewSerializer,
colorScheme: colorScheme,
newDoc: tr.doc,
intl: intl,
activeIndexPos: activeIndexPos
}, isExtendedEnabled(diffType) && {
isInserted: !isInserted,
diffType: diffType,
hideAddedDiffsUnderline: hideAddedDiffsUnderline,
placeBelow: isDeletedWidgetBelow
}), {}, {
showIndicators: showIndicators
}))));
}
}
};
changes.forEach(function (change) {
createDecorationsForChange(change);
});
getMarkChangeRanges(steps).forEach(function (change) {
var isActive = activeIndexPos && change.fromB === activeIndexPos.from && change.toB === activeIndexPos.to;
decorations.push.apply(decorations, _toConsumableArray(createInlineChangedDecoration({
change: change,
colorScheme: colorScheme,
isActive: isActive,
isInserted: true
})));
});
getAttrChangeRanges(tr.doc, attrSteps, originalDoc).forEach(function (change) {
if (change.isInline) {
// Inline nodes (e.g. date, emoji, mention, status) need an inline decoration rather than a block decoration
var isActive = activeIndexPos && change.fromB === activeIndexPos.from && change.toB === activeIndexPos.to;
var isAtomicInlineNodeFlag = isExperimentEnabled('platform_editor_improve_inline_diffs', function () {
return expValEquals('platform_editor_improve_inline_diffs', 'isEnabled', true);
});
decorations.push.apply(decorations, _toConsumableArray(createInlineChangedDecoration({
change: change,
colorScheme: colorScheme,
isActive: isActive,
isInserted: true,
isAtomicInlineNode: isAtomicInlineNodeFlag,
inlineNodeName: change.inlineNodeName,
// Suppress the border-bottom underline for atomic inline nodeviews —
// it doesn't render on custom nodeview DOM elements and is unnecessary noise.
hideAddedDiffsUnderline: isAtomicInlineNodeFlag,
diffType: diffType
})));
// If we have the original node position, also render the old node as a "deleted" widget
// so the user can see what it looked like before the change.
if (change.fromA !== undefined && change.toA !== undefined && isExperimentEnabled('platform_editor_improve_inline_diffs', function () {
return expValEquals('platform_editor_improve_inline_diffs', 'isEnabled', true);
})) {
decorations.push.apply(decorations, _toConsumableArray(createNodeChangedDecorationWidget({
change: {
fromA: change.fromA,
toA: change.toA,
fromB: change.fromB,
toB: change.toB,
deleted: []
},
doc: originalDoc,
nodeViewSerializer: nodeViewSerializer,
colorScheme: colorScheme,
newDoc: tr.doc,
intl: intl,
activeIndexPos: activeIndexPos,
isInserted: false
})));
}
} else {
decorations.push.apply(decorations, _toConsumableArray(calculateNodesForBlockDecoration({
doc: tr.doc,
from: change.fromB,
to: change.toB,
colorScheme: colorScheme,
isInserted: true,
activeIndexPos: activeIndexPos,
intl: intl,
showIndicators: showIndicators
})));
// If the original node position is known (e.g. a panel type change), also render
// the old block node as a "deleted" widget so the reviewer sees the before/after.
var isSmartNodeLevelAttrChange = diffType === 'smart' && fg('platform_editor_ai_smart_diff') && expValEquals('platform_editor_ai_new_aifc_editor_experience', 'isEnabled', true);
if (isSmartNodeLevelAttrChange && change.fromA !== undefined && change.toA !== undefined) {
var placeBelow = deletedDiffPlacement === 'bottom';
decorations.push.apply(decorations, _toConsumableArray(createNodeChangedDecorationWidget(_objectSpread(_objectSpread({
change: {
fromA: change.fromA,
toA: change.toA,
fromB: change.fromB,
toB: change.toB,
deleted: []
},
doc: originalDoc,
nodeViewSerializer: nodeViewSerializer,
colorScheme: colorScheme,
newDoc: tr.doc,
intl: intl,
activeIndexPos: activeIndexPos,
isInserted: false
}, isExtendedEnabled(diffType) && {
diffType: diffType,
hideAddedDiffsUnderline: hideAddedDiffsUnderline,
placeBelow: placeBelow
}), {}, {
showIndicators: showIndicators
}))));
}
}
});
var decorationSet = DecorationSet.empty.add(tr.doc, decorations);
return {
decorations: decorationSet,
diffDescriptors: extractDiffDescriptors(decorationSet)
};
};
export var calculateDiffDecorations = memoizeOne(calculateDiffDecorationsInner,
// Cache results unless relevant inputs change
function (_ref5, _ref6) {
var _ref0;
var _ref7 = _slicedToArray(_ref5, 1),
_ref7$ = _ref7[0],
pluginState = _ref7$.pluginState,
state = _ref7$.state,
colorScheme = _ref7$.colorScheme,
intl = _ref7$.intl,
activeIndexPos = _ref7$.activeIndexPos,
isInverted = _ref7$.isInverted,
diffType = _ref7$.diffType,
hideDeletedDiffs = _ref7$.hideDeletedDiffs,
hideAddedDiffsUnderline = _ref7$.hideAddedDiffsUnderline,
showIndicators = _ref7$.showIndicators,
smartThresholds = _ref7$.smartThresholds,
deletedDiffPlacement = _ref7$.deletedDiffPlacement,
inlineDeletedDiffPlacement = _ref7$.inlineDeletedDiffPlacement;
var _ref8 = _slicedToArray(_ref6, 1),
_ref8$ = _ref8[0],
lastPluginState = _ref8$.pluginState,
lastState = _ref8$.state,
lastColorScheme = _ref8$.colorScheme,
lastIntl = _ref8$.intl,
lastActiveIndexPos = _ref8$.activeIndexPos,
lastIsInverted = _ref8$.isInverted,
lastDiffType = _ref8$.diffType,
lastHideDeletedDiffs = _ref8$.hideDeletedDiffs,
lastHideAddedDiffsUnderline = _ref8$.hideAddedDiffsUnderline,
lastShowIndicators = _ref8$.showIndicators,
lastSmartThresholds = _ref8$.smartThresholds,
lastDeletedDiffPlacement = _ref8$.deletedDiffPlacement,
lastInlineDeletedDiffPlacement = _ref8$.inlineDeletedDiffPlacement;
var originalDocIsSame = lastPluginState.originalDoc && pluginState.originalDoc && pluginState.originalDoc.eq(lastPluginState.originalDoc);
if (isExtendedEnabled(diffType)) {
var _ref9;
return (_ref9 = 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 && hideAddedDiffsUnderline === lastHideAddedDiffsUnderline && showIndicators === lastShowIndicators && isEqual(smartThresholds, lastSmartThresholds) && deletedDiffPlacement === lastDeletedDiffPlacement && inlineDeletedDiffPlacement === lastInlineDeletedDiffPlacement) !== null && _ref9 !== void 0 ? _ref9 : false;
}
return (_ref0 = originalDocIsSame && isEqual(pluginState.steps, lastPluginState.steps) && state.doc.eq(lastState.doc) && colorScheme === lastColorScheme && intl.locale === lastIntl.locale && isEqual(activeIndexPos, lastActiveIndexPos) && hideDeletedDiffs === lastHideDeletedDiffs) !== null && _ref0 !== void 0 ? _ref0 : false;
});