UNPKG

@atlaskit/editor-plugin-show-diff

Version:

ShowDiff plugin for @atlaskit/editor-core

552 lines (543 loc) 27.6 kB
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; });