@atlaskit/editor-plugin-show-diff 10.1.23 → 10.2.0
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 +37 -0
- package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +132 -31
- package/dist/cjs/pm-plugins/calculateDiff/groupChangesByBlock.js +4 -2
- package/dist/cjs/pm-plugins/calculateDiff/smart/classifySmartChanges.js +841 -0
- package/dist/cjs/pm-plugins/calculateDiff/smart/helpers.js +135 -0
- package/dist/cjs/pm-plugins/calculateDiff/smart/segmentText.js +331 -0
- package/dist/cjs/pm-plugins/calculateDiff/smart/thresholds.js +46 -0
- package/dist/cjs/pm-plugins/decorations/createBlockChangedDecoration.js +24 -18
- package/dist/cjs/pm-plugins/decorations/createChangedRowDecorationWidgets.js +18 -12
- package/dist/cjs/pm-plugins/decorations/createInlineChangedDecoration.js +6 -5
- package/dist/cjs/pm-plugins/decorations/createNodeChangedDecorationWidget.js +30 -17
- package/dist/cjs/pm-plugins/decorations/decorationKeys.js +8 -5
- package/dist/cjs/pm-plugins/decorations/utils/wrapBlockNodeView.js +52 -30
- package/dist/cjs/pm-plugins/getScrollableDecorations.js +6 -6
- package/dist/cjs/pm-plugins/isExtendedEnabled.js +20 -0
- package/dist/cjs/pm-plugins/main.js +21 -16
- package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +119 -29
- package/dist/es2019/pm-plugins/calculateDiff/groupChangesByBlock.js +4 -3
- package/dist/es2019/pm-plugins/calculateDiff/smart/classifySmartChanges.js +689 -0
- package/dist/es2019/pm-plugins/calculateDiff/smart/helpers.js +106 -0
- package/dist/es2019/pm-plugins/calculateDiff/smart/segmentText.js +283 -0
- package/dist/es2019/pm-plugins/calculateDiff/smart/thresholds.js +45 -0
- package/dist/es2019/pm-plugins/decorations/createBlockChangedDecoration.js +24 -18
- package/dist/es2019/pm-plugins/decorations/createChangedRowDecorationWidgets.js +18 -12
- package/dist/es2019/pm-plugins/decorations/createInlineChangedDecoration.js +6 -5
- package/dist/es2019/pm-plugins/decorations/createNodeChangedDecorationWidget.js +31 -16
- package/dist/es2019/pm-plugins/decorations/decorationKeys.js +9 -5
- package/dist/es2019/pm-plugins/decorations/utils/wrapBlockNodeView.js +52 -34
- package/dist/es2019/pm-plugins/getScrollableDecorations.js +6 -6
- package/dist/es2019/pm-plugins/isExtendedEnabled.js +12 -0
- package/dist/es2019/pm-plugins/main.js +21 -16
- package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +132 -31
- package/dist/esm/pm-plugins/calculateDiff/groupChangesByBlock.js +4 -3
- package/dist/esm/pm-plugins/calculateDiff/smart/classifySmartChanges.js +834 -0
- package/dist/esm/pm-plugins/calculateDiff/smart/helpers.js +128 -0
- package/dist/esm/pm-plugins/calculateDiff/smart/segmentText.js +325 -0
- package/dist/esm/pm-plugins/calculateDiff/smart/thresholds.js +39 -0
- package/dist/esm/pm-plugins/decorations/createBlockChangedDecoration.js +24 -18
- package/dist/esm/pm-plugins/decorations/createChangedRowDecorationWidgets.js +18 -12
- package/dist/esm/pm-plugins/decorations/createInlineChangedDecoration.js +6 -5
- package/dist/esm/pm-plugins/decorations/createNodeChangedDecorationWidget.js +30 -17
- package/dist/esm/pm-plugins/decorations/decorationKeys.js +9 -5
- package/dist/esm/pm-plugins/decorations/utils/wrapBlockNodeView.js +52 -30
- package/dist/esm/pm-plugins/getScrollableDecorations.js +6 -6
- package/dist/esm/pm-plugins/isExtendedEnabled.js +14 -0
- package/dist/esm/pm-plugins/main.js +21 -16
- package/dist/types/entry-points/show-diff-plugin-type.d.ts +1 -1
- package/dist/types/pm-plugins/calculateDiff/calculateDiffDecorations.d.ts +3 -1
- package/dist/types/pm-plugins/calculateDiff/groupChangesByBlock.d.ts +9 -0
- package/dist/types/pm-plugins/calculateDiff/smart/classifySmartChanges.d.ts +26 -0
- package/dist/types/pm-plugins/calculateDiff/smart/helpers.d.ts +44 -0
- package/dist/types/pm-plugins/calculateDiff/smart/segmentText.d.ts +32 -0
- package/dist/types/pm-plugins/calculateDiff/smart/thresholds.d.ts +44 -0
- package/dist/types/pm-plugins/decorations/createBlockChangedDecoration.d.ts +3 -2
- package/dist/types/pm-plugins/decorations/createChangedRowDecorationWidgets.d.ts +3 -2
- package/dist/types/pm-plugins/decorations/createInlineChangedDecoration.d.ts +3 -2
- package/dist/types/pm-plugins/decorations/createNodeChangedDecorationWidget.d.ts +4 -2
- package/dist/types/pm-plugins/decorations/decorationKeys.d.ts +5 -3
- package/dist/types/pm-plugins/decorations/utils/wrapBlockNodeView.d.ts +6 -4
- package/dist/types/pm-plugins/getScrollableDecorations.d.ts +2 -1
- package/dist/types/pm-plugins/isExtendedEnabled.d.ts +11 -0
- package/dist/types/pm-plugins/main.d.ts +11 -1
- package/dist/types/showDiffPluginType.d.ts +20 -1
- package/docs/smart-diff-design.md +240 -0
- package/package.json +7 -2
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import _typeof from "@babel/runtime/helpers/typeof";
|
|
3
|
+
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
4
|
+
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; }
|
|
5
|
+
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; }
|
|
6
|
+
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; } } }; }
|
|
7
|
+
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; } }
|
|
8
|
+
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; }
|
|
9
|
+
/**
|
|
10
|
+
* The granularity level a `smart` change was promoted to. Stored on span `data` so the
|
|
11
|
+
* decoration pipeline can render each level differently (e.g. node-level deletions shown
|
|
12
|
+
* below the new content). `null` for non-promoted (inline) changes.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** Span `data` payload carried by promoted `smart` changes. */
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Build the `deleted`/`inserted` span arrays a `Change` needs. A length of 0 yields an
|
|
19
|
+
* empty array (no change on that side).
|
|
20
|
+
*/
|
|
21
|
+
export var createSpans = function createSpans(length) {
|
|
22
|
+
var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
|
23
|
+
return length > 0 ? [{
|
|
24
|
+
length: length,
|
|
25
|
+
data: data
|
|
26
|
+
}] : [];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Build a promoted `Change` spanning `[fromA,toA)` in the original doc and `[fromB,toB)`
|
|
31
|
+
* in the new doc, with both sides marked changed (mirrors `groupChangesByBlock` so the
|
|
32
|
+
* decoration pipeline renders both the inserted-block decoration and the deleted-node
|
|
33
|
+
* widget).
|
|
34
|
+
*
|
|
35
|
+
* `level` tags the change's spans so downstream decoration logic can treat node-level
|
|
36
|
+
* promotions specially (see `smartChangeLevel`).
|
|
37
|
+
*/
|
|
38
|
+
export var makePromotedChange = function makePromotedChange(fromA, toA, fromB, toB, level) {
|
|
39
|
+
var data = level ? {
|
|
40
|
+
smartLevel: level
|
|
41
|
+
} : null;
|
|
42
|
+
// `deleted` and `inserted` must be independent arrays: downstream code may mutate one
|
|
43
|
+
// side (push/splice), which would silently corrupt the other if they shared a reference.
|
|
44
|
+
return {
|
|
45
|
+
fromA: fromA,
|
|
46
|
+
toA: toA,
|
|
47
|
+
fromB: fromB,
|
|
48
|
+
toB: toB,
|
|
49
|
+
deleted: createSpans(Math.max(0, toA - fromA), data),
|
|
50
|
+
inserted: createSpans(Math.max(0, toB - fromB), data)
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Read the `smartLevel` tag off a change's spans, if present. Returns `undefined` for
|
|
56
|
+
* changes not produced by the `smart` classifier (e.g. inline/block/step diff types).
|
|
57
|
+
*/
|
|
58
|
+
export var smartChangeLevel = function smartChangeLevel(change) {
|
|
59
|
+
var spans = [].concat(_toConsumableArray(change.inserted), _toConsumableArray(change.deleted));
|
|
60
|
+
var _iterator = _createForOfIteratorHelper(spans),
|
|
61
|
+
_step;
|
|
62
|
+
try {
|
|
63
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
64
|
+
var span = _step.value;
|
|
65
|
+
var data = span.data;
|
|
66
|
+
if (data && _typeof(data) === 'object' && 'smartLevel' in data) {
|
|
67
|
+
return data.smartLevel;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} catch (err) {
|
|
71
|
+
_iterator.e(err);
|
|
72
|
+
} finally {
|
|
73
|
+
_iterator.f();
|
|
74
|
+
}
|
|
75
|
+
return undefined;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/** True when a change has no original-side (deleted) content — a pure insertion. */
|
|
79
|
+
var isPureInsertion = function isPureInsertion(change) {
|
|
80
|
+
return change.toA <= change.fromA;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Coalesce changes that overlap in new-doc (B) coordinates.
|
|
85
|
+
*
|
|
86
|
+
* Merging two changes takes the UNION of their A (original-doc) ranges. That is only safe when
|
|
87
|
+
* their A ranges actually touch/overlap — OR when at least one is a pure insertion (empty A).
|
|
88
|
+
* Otherwise, merging a deletion at A[110,152] with a later edit at A[162,168] would fabricate an
|
|
89
|
+
* A span [110,168] covering the untouched gap, which then overlaps a *different* change's A range
|
|
90
|
+
* (making the same original content appear deleted twice). So we require B-overlap AND
|
|
91
|
+
* (A-overlap OR a pure insertion) before coalescing.
|
|
92
|
+
*/
|
|
93
|
+
export var mergeOverlappingByNewDocRange = function mergeOverlappingByNewDocRange(changes) {
|
|
94
|
+
if (changes.length <= 1) {
|
|
95
|
+
return changes;
|
|
96
|
+
}
|
|
97
|
+
var sorted = _toConsumableArray(changes).sort(function (l, r) {
|
|
98
|
+
return l.fromB - r.fromB;
|
|
99
|
+
});
|
|
100
|
+
var merged = [];
|
|
101
|
+
var current = _objectSpread({}, sorted[0]);
|
|
102
|
+
for (var i = 1; i < sorted.length; i++) {
|
|
103
|
+
var next = sorted[i];
|
|
104
|
+
var bOverlaps = next.fromB <= current.toB;
|
|
105
|
+
// A ranges may be unioned only if they touch, or if either side contributes no A content.
|
|
106
|
+
var aMergeable = isPureInsertion(current) || isPureInsertion(next) || rangesOverlap(current.fromA, current.toA, next.fromA, next.toA) || next.fromA <= current.toA; // adjacency (sorted by B, A usually monotonic)
|
|
107
|
+
if (bOverlaps && aMergeable) {
|
|
108
|
+
current = {
|
|
109
|
+
fromA: Math.min(current.fromA, next.fromA),
|
|
110
|
+
toA: Math.max(current.toA, next.toA),
|
|
111
|
+
fromB: Math.min(current.fromB, next.fromB),
|
|
112
|
+
toB: Math.max(current.toB, next.toB),
|
|
113
|
+
deleted: [].concat(_toConsumableArray(current.deleted), _toConsumableArray(next.deleted)),
|
|
114
|
+
inserted: [].concat(_toConsumableArray(current.inserted), _toConsumableArray(next.inserted))
|
|
115
|
+
};
|
|
116
|
+
} else {
|
|
117
|
+
merged.push(current);
|
|
118
|
+
current = _objectSpread({}, next);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
merged.push(current);
|
|
122
|
+
return merged;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/** True when two half-open ranges overlap. */
|
|
126
|
+
export var rangesOverlap = function rangesOverlap(aFrom, aTo, bFrom, bTo) {
|
|
127
|
+
return aFrom < bTo && bFrom < aTo;
|
|
128
|
+
};
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
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; } } }; }
|
|
2
|
+
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; } }
|
|
3
|
+
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; }
|
|
4
|
+
/**
|
|
5
|
+
* A half-open offset span `[from, to)` measured in *content offsets* relative to the
|
|
6
|
+
* start of a textblock (i.e. the same coordinate space as `buildCharsByOffset`).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Minimal local typing for `Intl.Segmenter`. The TS lib target configured for this
|
|
11
|
+
* package does not include the `Intl.Segmenter` declarations, so we declare just the
|
|
12
|
+
* surface we use and access it via a runtime-checked cast. Remove when the lib target is
|
|
13
|
+
* bumped to include `es2022.intl`/`esnext.intl`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// A character is treated as part of a word unless it is whitespace or common punctuation.
|
|
17
|
+
// We deliberately avoid Unicode property escapes (`\p{...}`) and the `u` flag here: the `u`
|
|
18
|
+
// flag is only valid when targeting es6+, and the declaration-generation build config for
|
|
19
|
+
// this package targets an older lib, which rejects it with TS1501. This negated approach
|
|
20
|
+
// keeps word segmentation reasonable across scripts (any non-punctuation, non-space glyph
|
|
21
|
+
// counts as a word char) without needing Unicode-aware regex.
|
|
22
|
+
var NON_WORD_CHARS = ' \t\n\r\f\v.,;:!?…。!?、"\'`“”‘’()[]{}<>/\\|@#$%^&*-+=~';
|
|
23
|
+
var NON_WORD_CHAR_SET = new Set(NON_WORD_CHARS.split(''));
|
|
24
|
+
var isWordChar = function isWordChar(ch) {
|
|
25
|
+
return !NON_WORD_CHAR_SET.has(ch);
|
|
26
|
+
};
|
|
27
|
+
// Whitespace is checked via a Set rather than a regex to avoid both the `require-unicode-regexp`
|
|
28
|
+
// lint rule and the TS1501 error the `u` flag triggers under this package's build lib target.
|
|
29
|
+
var WHITESPACE_CHAR_SET = new Set([' ', '\t', '\n', '\r', '\f', '\v', "\xA0"]);
|
|
30
|
+
var isWhitespaceChar = function isWhitespaceChar(ch) {
|
|
31
|
+
return WHITESPACE_CHAR_SET.has(ch);
|
|
32
|
+
};
|
|
33
|
+
// Sentence terminators used by the regex fallback. We intentionally keep this small and
|
|
34
|
+
// conservative — the Intl.Segmenter path is the source of truth when available.
|
|
35
|
+
var SENTENCE_TERMINATORS = new Set(['.', '!', '?', '…', '。', '!', '?']);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Build a per-content-offset view of a textblock's characters.
|
|
39
|
+
*
|
|
40
|
+
* `chars[i]` is the character at content offset `i`, or `null` when that offset lies
|
|
41
|
+
* inside a non-text inline node (mention, date, emoji, hardBreak, …). A `null` acts as an
|
|
42
|
+
* *opaque single token*: it counts as one word and never terminates a sentence.
|
|
43
|
+
*/
|
|
44
|
+
export var buildCharsByOffset = function buildCharsByOffset(parent) {
|
|
45
|
+
var chars = Array.from({
|
|
46
|
+
length: parent.content.size
|
|
47
|
+
}, function () {
|
|
48
|
+
return null;
|
|
49
|
+
});
|
|
50
|
+
parent.content.forEach(function (child, offset) {
|
|
51
|
+
var _child$text;
|
|
52
|
+
if (!child.isText) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
var text = (_child$text = child.text) !== null && _child$text !== void 0 ? _child$text : '';
|
|
56
|
+
for (var i = 0; i < text.length; i++) {
|
|
57
|
+
chars[offset + i] = text[i];
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
return chars;
|
|
61
|
+
};
|
|
62
|
+
var getSegmenterCtor = function getSegmenterCtor() {
|
|
63
|
+
if (typeof Intl === 'undefined') {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
var ctor = Intl.Segmenter;
|
|
67
|
+
return typeof ctor === 'function' ? ctor : undefined;
|
|
68
|
+
};
|
|
69
|
+
var cachedLocale;
|
|
70
|
+
var cachedSentenceSegmenter;
|
|
71
|
+
var cachedWordSegmenter;
|
|
72
|
+
var getSegmenters = function getSegmenters(locale) {
|
|
73
|
+
var Ctor = getSegmenterCtor();
|
|
74
|
+
if (!Ctor) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
if (cachedLocale !== locale || !cachedSentenceSegmenter || !cachedWordSegmenter) {
|
|
78
|
+
cachedLocale = locale;
|
|
79
|
+
cachedSentenceSegmenter = new Ctor(locale, {
|
|
80
|
+
granularity: 'sentence'
|
|
81
|
+
});
|
|
82
|
+
cachedWordSegmenter = new Ctor(locale, {
|
|
83
|
+
granularity: 'word'
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
sentence: cachedSentenceSegmenter,
|
|
88
|
+
word: cachedWordSegmenter
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Convert a char-view into a plain string plus an index map back to content offsets.
|
|
94
|
+
*
|
|
95
|
+
* Opaque inline tokens (`null`) are represented in the string by a single sentinel
|
|
96
|
+
* character (Unicode Object Replacement Character, U+FFFC) so that:
|
|
97
|
+
* - the segmenter treats them as a word-like glyph rather than whitespace/punctuation;
|
|
98
|
+
* - they never look like a sentence terminator.
|
|
99
|
+
* `indexMap[j]` gives the content offset of string index `j`.
|
|
100
|
+
*/
|
|
101
|
+
var OBJECT_REPLACEMENT = "\uFFFC";
|
|
102
|
+
var flattenChars = function flattenChars(chars) {
|
|
103
|
+
var text = '';
|
|
104
|
+
var indexMap = [];
|
|
105
|
+
for (var offset = 0; offset < chars.length; offset++) {
|
|
106
|
+
var ch = chars[offset];
|
|
107
|
+
text += ch === null ? OBJECT_REPLACEMENT : ch;
|
|
108
|
+
indexMap.push(offset);
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
text: text,
|
|
112
|
+
indexMap: indexMap
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
var stringIndexToOffset = function stringIndexToOffset(indexMap, strIdx, fallback) {
|
|
116
|
+
if (strIdx < 0) {
|
|
117
|
+
return fallback;
|
|
118
|
+
}
|
|
119
|
+
if (strIdx >= indexMap.length) {
|
|
120
|
+
// One-past-the-end maps to the offset after the last char.
|
|
121
|
+
return indexMap.length > 0 ? indexMap[indexMap.length - 1] + 1 : fallback;
|
|
122
|
+
}
|
|
123
|
+
return indexMap[strIdx];
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Segment a textblock char-view into sentence spans (content-offset coordinates).
|
|
128
|
+
* Uses Intl.Segmenter when available, otherwise a conservative regex fallback.
|
|
129
|
+
*/
|
|
130
|
+
export var segmentSentences = function segmentSentences(chars, locale) {
|
|
131
|
+
if (chars.length === 0) {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
var _flattenChars = flattenChars(chars),
|
|
135
|
+
text = _flattenChars.text,
|
|
136
|
+
indexMap = _flattenChars.indexMap;
|
|
137
|
+
var segmenters = getSegmenters(locale);
|
|
138
|
+
if (segmenters) {
|
|
139
|
+
var _spans = [];
|
|
140
|
+
var _iterator = _createForOfIteratorHelper(segmenters.sentence.segment(text)),
|
|
141
|
+
_step;
|
|
142
|
+
try {
|
|
143
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
144
|
+
var seg = _step.value;
|
|
145
|
+
var startOffset = stringIndexToOffset(indexMap, seg.index, 0);
|
|
146
|
+
var endOffset = stringIndexToOffset(indexMap, seg.index + seg.segment.length, chars.length);
|
|
147
|
+
if (endOffset > startOffset) {
|
|
148
|
+
_spans.push({
|
|
149
|
+
from: startOffset,
|
|
150
|
+
to: endOffset
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
} catch (err) {
|
|
155
|
+
_iterator.e(err);
|
|
156
|
+
} finally {
|
|
157
|
+
_iterator.f();
|
|
158
|
+
}
|
|
159
|
+
return _spans.length > 0 ? _spans : [{
|
|
160
|
+
from: 0,
|
|
161
|
+
to: chars.length
|
|
162
|
+
}];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Regex fallback: split after a sentence terminator followed by whitespace/EOL.
|
|
166
|
+
// A `null` (OBJECT_REPLACEMENT) is never a terminator.
|
|
167
|
+
var spans = [];
|
|
168
|
+
var start = 0;
|
|
169
|
+
for (var i = 0; i < chars.length; i++) {
|
|
170
|
+
var ch = chars[i];
|
|
171
|
+
if (ch !== null && SENTENCE_TERMINATORS.has(ch)) {
|
|
172
|
+
// Consume trailing spaces as part of this sentence.
|
|
173
|
+
var end = i + 1;
|
|
174
|
+
while (end < chars.length && chars[end] !== null && isWhitespaceChar(chars[end])) {
|
|
175
|
+
end++;
|
|
176
|
+
}
|
|
177
|
+
spans.push({
|
|
178
|
+
from: start,
|
|
179
|
+
to: end
|
|
180
|
+
});
|
|
181
|
+
start = end;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (start < chars.length) {
|
|
185
|
+
spans.push({
|
|
186
|
+
from: start,
|
|
187
|
+
to: chars.length
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
return spans.length > 0 ? spans : [{
|
|
191
|
+
from: 0,
|
|
192
|
+
to: chars.length
|
|
193
|
+
}];
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Count word-like tokens within a content-offset range of the char-view.
|
|
198
|
+
* Each opaque inline token (`null`) counts as exactly one word.
|
|
199
|
+
*/
|
|
200
|
+
export var countWords = function countWords(chars, span, locale) {
|
|
201
|
+
var slice = chars.slice(span.from, span.to);
|
|
202
|
+
if (slice.length === 0) {
|
|
203
|
+
return 0;
|
|
204
|
+
}
|
|
205
|
+
var segmenters = getSegmenters(locale);
|
|
206
|
+
var _flattenChars2 = flattenChars(slice),
|
|
207
|
+
text = _flattenChars2.text;
|
|
208
|
+
if (segmenters) {
|
|
209
|
+
var _count = 0;
|
|
210
|
+
var _iterator2 = _createForOfIteratorHelper(segmenters.word.segment(text)),
|
|
211
|
+
_step2;
|
|
212
|
+
try {
|
|
213
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
214
|
+
var seg = _step2.value;
|
|
215
|
+
// Object-replacement sentinel is word-like; count it.
|
|
216
|
+
if (seg.isWordLike || seg.segment.includes(OBJECT_REPLACEMENT)) {
|
|
217
|
+
_count++;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
} catch (err) {
|
|
221
|
+
_iterator2.e(err);
|
|
222
|
+
} finally {
|
|
223
|
+
_iterator2.f();
|
|
224
|
+
}
|
|
225
|
+
return _count;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Regex fallback: a run of word chars OR a single opaque token is one word.
|
|
229
|
+
var count = 0;
|
|
230
|
+
var inWord = false;
|
|
231
|
+
var _iterator3 = _createForOfIteratorHelper(slice),
|
|
232
|
+
_step3;
|
|
233
|
+
try {
|
|
234
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
235
|
+
var ch = _step3.value;
|
|
236
|
+
if (ch === null) {
|
|
237
|
+
count++;
|
|
238
|
+
inWord = false;
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
if (isWordChar(ch)) {
|
|
242
|
+
if (!inWord) {
|
|
243
|
+
count++;
|
|
244
|
+
inWord = true;
|
|
245
|
+
}
|
|
246
|
+
} else {
|
|
247
|
+
inWord = false;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
} catch (err) {
|
|
251
|
+
_iterator3.e(err);
|
|
252
|
+
} finally {
|
|
253
|
+
_iterator3.f();
|
|
254
|
+
}
|
|
255
|
+
return count;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Return the word-token spans (content-offset coordinates) within a range. Used to decide
|
|
260
|
+
* which words a change overlaps. Each opaque inline token is its own span.
|
|
261
|
+
*/
|
|
262
|
+
export var segmentWordSpans = function segmentWordSpans(chars, span, locale) {
|
|
263
|
+
var segmenters = getSegmenters(locale);
|
|
264
|
+
var slice = chars.slice(span.from, span.to);
|
|
265
|
+
var _flattenChars3 = flattenChars(slice),
|
|
266
|
+
text = _flattenChars3.text,
|
|
267
|
+
indexMap = _flattenChars3.indexMap;
|
|
268
|
+
var spans = [];
|
|
269
|
+
if (segmenters) {
|
|
270
|
+
var _iterator4 = _createForOfIteratorHelper(segmenters.word.segment(text)),
|
|
271
|
+
_step4;
|
|
272
|
+
try {
|
|
273
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
274
|
+
var seg = _step4.value;
|
|
275
|
+
if (seg.isWordLike || seg.segment.includes(OBJECT_REPLACEMENT)) {
|
|
276
|
+
var from = span.from + stringIndexToOffset(indexMap, seg.index, 0);
|
|
277
|
+
var to = span.from + stringIndexToOffset(indexMap, seg.index + seg.segment.length, slice.length);
|
|
278
|
+
spans.push({
|
|
279
|
+
from: from,
|
|
280
|
+
to: to
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
} catch (err) {
|
|
285
|
+
_iterator4.e(err);
|
|
286
|
+
} finally {
|
|
287
|
+
_iterator4.f();
|
|
288
|
+
}
|
|
289
|
+
return spans;
|
|
290
|
+
}
|
|
291
|
+
var wordStart = -1;
|
|
292
|
+
for (var i = 0; i < slice.length; i++) {
|
|
293
|
+
var ch = slice[i];
|
|
294
|
+
if (ch === null) {
|
|
295
|
+
if (wordStart !== -1) {
|
|
296
|
+
spans.push({
|
|
297
|
+
from: span.from + wordStart,
|
|
298
|
+
to: span.from + i
|
|
299
|
+
});
|
|
300
|
+
wordStart = -1;
|
|
301
|
+
}
|
|
302
|
+
spans.push({
|
|
303
|
+
from: span.from + i,
|
|
304
|
+
to: span.from + i + 1
|
|
305
|
+
});
|
|
306
|
+
} else if (isWordChar(ch)) {
|
|
307
|
+
if (wordStart === -1) {
|
|
308
|
+
wordStart = i;
|
|
309
|
+
}
|
|
310
|
+
} else if (wordStart !== -1) {
|
|
311
|
+
spans.push({
|
|
312
|
+
from: span.from + wordStart,
|
|
313
|
+
to: span.from + i
|
|
314
|
+
});
|
|
315
|
+
wordStart = -1;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (wordStart !== -1) {
|
|
319
|
+
spans.push({
|
|
320
|
+
from: span.from + wordStart,
|
|
321
|
+
to: span.from + slice.length
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
return spans;
|
|
325
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
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; }
|
|
3
|
+
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; }
|
|
4
|
+
/**
|
|
5
|
+
* Configurable thresholds for the `smart` diffType.
|
|
6
|
+
*
|
|
7
|
+
* See docs/smart-diff-design.md §3 and §6 for the exact semantics of each level.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export var DEFAULT_SMART_THRESHOLDS = {
|
|
11
|
+
sentence: {
|
|
12
|
+
ratio: 0.4,
|
|
13
|
+
minChanged: 2
|
|
14
|
+
},
|
|
15
|
+
paragraph: {
|
|
16
|
+
ratio: 0.4,
|
|
17
|
+
minChanged: 2
|
|
18
|
+
},
|
|
19
|
+
node: {
|
|
20
|
+
ratio: 0.6,
|
|
21
|
+
textBearingRatio: 0.6
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Merge caller overrides onto the defaults. Overrides are shallow-merged per level so a
|
|
27
|
+
* caller can tune a single field (e.g. only `sentence.ratio`) without re-specifying the
|
|
28
|
+
* rest.
|
|
29
|
+
*/
|
|
30
|
+
export var resolveThresholds = function resolveThresholds(overrides) {
|
|
31
|
+
if (!overrides) {
|
|
32
|
+
return DEFAULT_SMART_THRESHOLDS;
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
sentence: _objectSpread(_objectSpread({}, DEFAULT_SMART_THRESHOLDS.sentence), overrides.sentence),
|
|
36
|
+
paragraph: _objectSpread(_objectSpread({}, DEFAULT_SMART_THRESHOLDS.paragraph), overrides.paragraph),
|
|
37
|
+
node: _objectSpread(_objectSpread({}, DEFAULT_SMART_THRESHOLDS.node), overrides.node)
|
|
38
|
+
};
|
|
39
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
2
2
|
import { convertToInlineCss } from '@atlaskit/editor-common/lazy-node-view';
|
|
3
3
|
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
4
|
-
import {
|
|
4
|
+
import { isExtendedEnabled } from '../isExtendedEnabled';
|
|
5
5
|
import { standardDecorationMarkerVariable, deletedDecorationMarkerVariable, editingStyleQuoteNode, editingStyleRuleNode, editingStyleCardBlockNode, editingStyleNode, deletedContentStyleNew, deletedStyleQuoteNode, addedCellOverlayStyle, deletedCellOverlayStyle } from './colorSchemes/standard';
|
|
6
6
|
import { traditionalDecorationMarkerVariableActive, traditionalDecorationMarkerVariableNew, traditionalDeletedDecorationMarkerVariableActive, traditionalDeletedDecorationMarkerVariableNew, traditionalStyleQuoteNodeActive, traditionalStyleQuoteNodeNew, traditionalStyleRuleNodeActive, traditionalStyleRuleNodeNew, traditionalStyleCardBlockNodeActive, traditionalStyleCardBlockNodeNew, traditionalStyleNodeActive, traditionalStyleNodeNew, getDeletedTraditionalInlineStyle, deletedTraditionalStyleQuoteNode, traditionalAddedCellOverlayStyle, deletedTraditionalCellOverlayStyle } from './colorSchemes/traditional';
|
|
7
7
|
import { createBlockIndicatorAnchorWidgets } from './createAnchorDecorationWidgets';
|
|
@@ -23,7 +23,8 @@ var getBlockNodeStyle = function getBlockNodeStyle(_ref) {
|
|
|
23
23
|
_ref$isInserted = _ref.isInserted,
|
|
24
24
|
isInserted = _ref$isInserted === void 0 ? true : _ref$isInserted,
|
|
25
25
|
_ref$isActive = _ref.isActive,
|
|
26
|
-
isActive = _ref$isActive === void 0 ? false : _ref$isActive
|
|
26
|
+
isActive = _ref$isActive === void 0 ? false : _ref$isActive,
|
|
27
|
+
diffType = _ref.diffType;
|
|
27
28
|
var isTraditional = colorScheme === 'traditional';
|
|
28
29
|
if (['mediaSingle', 'mediaGroup', 'table',
|
|
29
30
|
// Handle table separately to avoid border issues
|
|
@@ -36,13 +37,13 @@ var getBlockNodeStyle = function getBlockNodeStyle(_ref) {
|
|
|
36
37
|
// Media nodes inside mediaSingle should not get position:relative
|
|
37
38
|
// as it shifts the image outside its parent container (e.g. panel)
|
|
38
39
|
if (nodeName === 'media') {
|
|
39
|
-
if (!isInserted &&
|
|
40
|
+
if (!isInserted && isExtendedEnabled(diffType)) {
|
|
40
41
|
return isTraditional ? getDeletedTraditionalInlineStyle(false) : deletedDecorationMarkerVariable;
|
|
41
42
|
}
|
|
42
43
|
return isTraditional ? isActive ? traditionalStyleNodeActive : traditionalStyleNodeNew : editingStyleNode;
|
|
43
44
|
}
|
|
44
45
|
if (['tableCell', 'tableHeader'].includes(nodeName)) {
|
|
45
|
-
if (
|
|
46
|
+
if (isExtendedEnabled(diffType)) {
|
|
46
47
|
// This is used for positioning the cell overlay widget decorations
|
|
47
48
|
return convertToInlineCss({
|
|
48
49
|
position: 'relative'
|
|
@@ -52,13 +53,13 @@ var getBlockNodeStyle = function getBlockNodeStyle(_ref) {
|
|
|
52
53
|
return undefined;
|
|
53
54
|
}
|
|
54
55
|
if (nodeName === 'panel') {
|
|
55
|
-
if (!isInserted &&
|
|
56
|
+
if (!isInserted && isExtendedEnabled(diffType)) {
|
|
56
57
|
return isTraditional ? getDeletedTraditionalInlineStyle(false) : deletedDecorationMarkerVariable;
|
|
57
58
|
}
|
|
58
59
|
return isTraditional ? isActive ? traditionalStyleNodeActive : traditionalStyleNodeNew : editingStyleNode;
|
|
59
60
|
}
|
|
60
61
|
if (['extension', 'embedCard', 'listItem'].includes(nodeName)) {
|
|
61
|
-
if (
|
|
62
|
+
if (isExtendedEnabled(diffType)) {
|
|
62
63
|
if (isInserted) {
|
|
63
64
|
return isTraditional && isActive ? traditionalDecorationMarkerVariableActive : isTraditional ? traditionalDecorationMarkerVariableNew : standardDecorationMarkerVariable;
|
|
64
65
|
} else {
|
|
@@ -71,7 +72,7 @@ var getBlockNodeStyle = function getBlockNodeStyle(_ref) {
|
|
|
71
72
|
return isTraditional && isActive ? traditionalDecorationMarkerVariableActive : isTraditional ? traditionalDecorationMarkerVariableNew : standardDecorationMarkerVariable;
|
|
72
73
|
}
|
|
73
74
|
if (nodeName === 'blockquote') {
|
|
74
|
-
if (
|
|
75
|
+
if (isExtendedEnabled(diffType)) {
|
|
75
76
|
if (isInserted) {
|
|
76
77
|
return isTraditional ? isActive ? traditionalStyleQuoteNodeActive : traditionalStyleQuoteNodeNew : editingStyleQuoteNode;
|
|
77
78
|
} else {
|
|
@@ -81,7 +82,7 @@ var getBlockNodeStyle = function getBlockNodeStyle(_ref) {
|
|
|
81
82
|
return isTraditional ? isActive ? traditionalStyleQuoteNodeActive : traditionalStyleQuoteNodeNew : editingStyleQuoteNode;
|
|
82
83
|
}
|
|
83
84
|
if (nodeName === 'rule') {
|
|
84
|
-
if (
|
|
85
|
+
if (isExtendedEnabled(diffType)) {
|
|
85
86
|
if (isInserted) {
|
|
86
87
|
return isTraditional ? isActive ? traditionalStyleRuleNodeActive : traditionalStyleRuleNodeNew : editingStyleRuleNode;
|
|
87
88
|
} else {
|
|
@@ -91,7 +92,7 @@ var getBlockNodeStyle = function getBlockNodeStyle(_ref) {
|
|
|
91
92
|
return isTraditional ? isActive ? traditionalStyleRuleNodeActive : traditionalStyleRuleNodeNew : editingStyleRuleNode;
|
|
92
93
|
}
|
|
93
94
|
if (nodeName === 'blockCard') {
|
|
94
|
-
if (
|
|
95
|
+
if (isExtendedEnabled(diffType)) {
|
|
95
96
|
if (isInserted) {
|
|
96
97
|
return isTraditional ? isActive ? traditionalStyleCardBlockNodeActive : traditionalStyleCardBlockNodeNew : editingStyleCardBlockNode;
|
|
97
98
|
} else {
|
|
@@ -100,7 +101,7 @@ var getBlockNodeStyle = function getBlockNodeStyle(_ref) {
|
|
|
100
101
|
}
|
|
101
102
|
return isTraditional ? isActive ? traditionalStyleCardBlockNodeActive : traditionalStyleCardBlockNodeNew : editingStyleCardBlockNode;
|
|
102
103
|
}
|
|
103
|
-
if (
|
|
104
|
+
if (isExtendedEnabled(diffType)) {
|
|
104
105
|
if (isInserted) {
|
|
105
106
|
return isTraditional ? isActive ? traditionalStyleNodeActive : traditionalStyleNodeNew : editingStyleNode;
|
|
106
107
|
} else {
|
|
@@ -129,7 +130,8 @@ export var createBlockChangedDecoration = function createBlockChangedDecoration(
|
|
|
129
130
|
shouldHideDeleted = _ref2$shouldHideDelet === void 0 ? false : _ref2$shouldHideDelet,
|
|
130
131
|
_ref2$showIndicators = _ref2.showIndicators,
|
|
131
132
|
showIndicators = _ref2$showIndicators === void 0 ? false : _ref2$showIndicators,
|
|
132
|
-
doc = _ref2.doc
|
|
133
|
+
doc = _ref2.doc,
|
|
134
|
+
diffType = _ref2.diffType;
|
|
133
135
|
var decorations = [];
|
|
134
136
|
var diffId = crypto.randomUUID();
|
|
135
137
|
if (shouldHideDeleted) {
|
|
@@ -139,11 +141,12 @@ export var createBlockChangedDecoration = function createBlockChangedDecoration(
|
|
|
139
141
|
decorationType: 'block',
|
|
140
142
|
diffId: diffId,
|
|
141
143
|
isActive: isActive,
|
|
142
|
-
nodeName: change.name
|
|
144
|
+
nodeName: change.name,
|
|
145
|
+
diffType: diffType
|
|
143
146
|
}))];
|
|
144
147
|
}
|
|
145
148
|
var style;
|
|
146
|
-
if (
|
|
149
|
+
if (isExtendedEnabled(diffType) && ['tableCell', 'tableHeader'].includes(change.name)) {
|
|
147
150
|
var cellOverlay = document.createElement('div');
|
|
148
151
|
var cellOverlayStyle = isInserted ? colorScheme === 'traditional' ? traditionalAddedCellOverlayStyle : addedCellOverlayStyle : colorScheme === 'traditional' ? deletedTraditionalCellOverlayStyle : deletedCellOverlayStyle;
|
|
149
152
|
cellOverlay.setAttribute('style', cellOverlayStyle);
|
|
@@ -155,18 +158,20 @@ export var createBlockChangedDecoration = function createBlockChangedDecoration(
|
|
|
155
158
|
key: 'cell-overlay-decoration'
|
|
156
159
|
}));
|
|
157
160
|
}
|
|
158
|
-
if (
|
|
161
|
+
if (isExtendedEnabled(diffType)) {
|
|
159
162
|
style = getBlockNodeStyle({
|
|
160
163
|
nodeName: change.name,
|
|
161
164
|
colorScheme: colorScheme,
|
|
162
165
|
isInserted: isInserted,
|
|
163
|
-
isActive: isActive
|
|
166
|
+
isActive: isActive,
|
|
167
|
+
diffType: diffType
|
|
164
168
|
});
|
|
165
169
|
} else {
|
|
166
170
|
style = getBlockNodeStyle({
|
|
167
171
|
nodeName: change.name,
|
|
168
172
|
colorScheme: colorScheme,
|
|
169
|
-
isActive: isActive
|
|
173
|
+
isActive: isActive,
|
|
174
|
+
diffType: diffType
|
|
170
175
|
});
|
|
171
176
|
}
|
|
172
177
|
var className = getNodeClass(change.name);
|
|
@@ -179,10 +184,11 @@ export var createBlockChangedDecoration = function createBlockChangedDecoration(
|
|
|
179
184
|
decorationType: 'block',
|
|
180
185
|
diffId: diffId,
|
|
181
186
|
isActive: isActive,
|
|
182
|
-
nodeName: change.name
|
|
187
|
+
nodeName: change.name,
|
|
188
|
+
diffType: diffType
|
|
183
189
|
})));
|
|
184
190
|
}
|
|
185
|
-
if (decorations.length > 0 && showIndicators && doc &&
|
|
191
|
+
if (decorations.length > 0 && showIndicators && doc && isExtendedEnabled(diffType)) {
|
|
186
192
|
decorations.push.apply(decorations, _toConsumableArray(createBlockIndicatorAnchorWidgets({
|
|
187
193
|
doc: doc,
|
|
188
194
|
from: change.from,
|