@atlaskit/editor-plugin-show-diff 15.1.4 → 15.1.5
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 +14 -0
- package/dist/cjs/pm-plugins/calculateDiff/diffBySteps.js +2 -31
- package/dist/cjs/pm-plugins/calculateDiff/smart/classifySmartChanges.js +3 -2
- package/dist/cjs/pm-plugins/calculateDiff/smart/segmentText.js +3 -34
- package/dist/cjs/pm-plugins/decorations/createContributorTagWidget.js +49 -23
- package/dist/cjs/pm-plugins/decorations/createInlineChangedDecoration.js +9 -2
- package/dist/cjs/pm-plugins/decorations/createNodeChangedDecorationWidget.js +7 -2
- package/dist/cjs/pm-plugins/decorations/extractContributorTags.js +38 -10
- package/dist/cjs/pm-plugins/utils/charsByOffset.js +45 -0
- package/dist/cjs/pm-plugins/utils/hasVisibleContent.js +37 -0
- package/dist/cjs/ui/ContributorTag/contributorTagController.js +1 -1
- package/dist/cjs/ui/IndicatorBar/IndicatorBar.js +1 -1
- package/dist/cjs/ui/IndicatorBar/IndicatorBarContentComponent.js +1 -1
- package/dist/es2019/pm-plugins/calculateDiff/diffBySteps.js +1 -30
- package/dist/es2019/pm-plugins/calculateDiff/smart/classifySmartChanges.js +2 -1
- package/dist/es2019/pm-plugins/calculateDiff/smart/segmentText.js +2 -28
- package/dist/es2019/pm-plugins/decorations/createContributorTagWidget.js +40 -15
- package/dist/es2019/pm-plugins/decorations/createInlineChangedDecoration.js +10 -3
- package/dist/es2019/pm-plugins/decorations/createNodeChangedDecorationWidget.js +7 -2
- package/dist/es2019/pm-plugins/decorations/extractContributorTags.js +27 -3
- package/dist/es2019/pm-plugins/utils/charsByOffset.js +37 -0
- package/dist/es2019/pm-plugins/utils/hasVisibleContent.js +28 -0
- package/dist/es2019/ui/ContributorTag/contributorTagController.js +1 -1
- package/dist/es2019/ui/IndicatorBar/IndicatorBar.js +1 -1
- package/dist/es2019/ui/IndicatorBar/IndicatorBarContentComponent.js +1 -1
- package/dist/esm/pm-plugins/calculateDiff/diffBySteps.js +1 -30
- package/dist/esm/pm-plugins/calculateDiff/smart/classifySmartChanges.js +2 -1
- package/dist/esm/pm-plugins/calculateDiff/smart/segmentText.js +2 -32
- package/dist/esm/pm-plugins/decorations/createContributorTagWidget.js +48 -22
- package/dist/esm/pm-plugins/decorations/createInlineChangedDecoration.js +10 -3
- package/dist/esm/pm-plugins/decorations/createNodeChangedDecorationWidget.js +7 -2
- package/dist/esm/pm-plugins/decorations/extractContributorTags.js +38 -10
- package/dist/esm/pm-plugins/utils/charsByOffset.js +39 -0
- package/dist/esm/pm-plugins/utils/hasVisibleContent.js +32 -0
- package/dist/esm/ui/ContributorTag/contributorTagController.js +1 -1
- package/dist/esm/ui/IndicatorBar/IndicatorBar.js +1 -1
- package/dist/esm/ui/IndicatorBar/IndicatorBarContentComponent.js +1 -1
- package/dist/types/pm-plugins/calculateDiff/smart/segmentText.d.ts +0 -9
- package/dist/types/pm-plugins/decorations/createContributorTagWidget.d.ts +4 -3
- package/dist/types/pm-plugins/decorations/extractContributorTags.d.ts +1 -1
- package/dist/types/pm-plugins/utils/charsByOffset.d.ts +16 -0
- package/dist/types/pm-plugins/utils/hasVisibleContent.d.ts +13 -0
- package/dist/types/ui/ContributorTag/contributorTagController.d.ts +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-show-diff
|
|
2
2
|
|
|
3
|
+
## 15.1.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`2772d8de10083`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/2772d8de10083) -
|
|
8
|
+
Anchor the diff contributor tag on the first non-whitespace character of a change, instead of on
|
|
9
|
+
its first character, so a change whose range opens on whitespace is captioned on the text it
|
|
10
|
+
changed. Also move a replacement's tag onto its deleted half whenever that half renders in front
|
|
11
|
+
of the inserted text, so the tag consistently marks where the change begins. A change whose
|
|
12
|
+
content is nothing but whitespace — a space typed between two words, a run of trailing spaces, an
|
|
13
|
+
added blank line — now carries no tag at all, since it has no visible content to caption; the
|
|
14
|
+
change itself is still highlighted. Behind the `confluence_ncs_step_diffing_version_history` gate.
|
|
15
|
+
- Updated dependencies
|
|
16
|
+
|
|
3
17
|
## 15.1.4
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
|
@@ -12,6 +12,7 @@ var _prosemirrorChangeset = require("prosemirror-changeset");
|
|
|
12
12
|
var _model = require("@atlaskit/editor-prosemirror/model");
|
|
13
13
|
var _transform = require("@atlaskit/editor-prosemirror/transform");
|
|
14
14
|
var _attributions = require("../decorations/colorSchemes/attributions");
|
|
15
|
+
var _charsByOffset = require("../utils/charsByOffset");
|
|
15
16
|
var _attrAwareTokenEncoder = require("./attrAwareTokenEncoder");
|
|
16
17
|
var _optimizeChanges = require("./optimizeChanges");
|
|
17
18
|
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; }
|
|
@@ -26,36 +27,6 @@ var mapPosition = function mapPosition(mapping, pos) {
|
|
|
26
27
|
return mapping.map(pos);
|
|
27
28
|
};
|
|
28
29
|
|
|
29
|
-
/**
|
|
30
|
-
* Build a per-content-offset view of the textblock's characters.
|
|
31
|
-
*
|
|
32
|
-
* Returns an array `chars` whose length is `parent.content.size`. For every
|
|
33
|
-
* offset that lies inside a text node, `chars[offset]` is the character at
|
|
34
|
-
* that offset; for every offset that lies inside (or on the edge of) a
|
|
35
|
-
* non-text inline node — hardBreak, mention, emoji, date, etc. — the entry
|
|
36
|
-
* is `null`.
|
|
37
|
-
*
|
|
38
|
-
* Using doc positions to index `parent.textContent` is wrong because
|
|
39
|
-
* `textContent` strips non-text inline nodes, so every such node shifts the
|
|
40
|
-
* lookup off by its size. This per-offset view restores a 1:1 mapping between
|
|
41
|
-
* doc positions inside the textblock and the character (or "no character",
|
|
42
|
-
* i.e. a hard word boundary) at that position.
|
|
43
|
-
*/
|
|
44
|
-
var buildCharsByOffset = function buildCharsByOffset(parent) {
|
|
45
|
-
var chars = new Array(parent.content.size).fill(null);
|
|
46
|
-
parent.content.forEach(function (child, offset) {
|
|
47
|
-
var _child$text;
|
|
48
|
-
if (!child.isText) {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
var text = (_child$text = child.text) !== null && _child$text !== void 0 ? _child$text : '';
|
|
52
|
-
for (var i = 0; i < text.length; i++) {
|
|
53
|
-
chars[offset + i] = text[i];
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
return chars;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
30
|
/**
|
|
60
31
|
* Given a ProseMirror doc and a position range [from, to], expand
|
|
61
32
|
* both endpoints outward to the nearest word boundaries.
|
|
@@ -99,7 +70,7 @@ var expandToWordBoundaries = function expandToWordBoundaries(doc, from, to) {
|
|
|
99
70
|
// Per-offset view of the textblock so we don't conflate the inline
|
|
100
71
|
// positions of non-text nodes (hardBreak, mention, emoji, date, etc.)
|
|
101
72
|
// with the characters returned by `parent.textContent`.
|
|
102
|
-
var chars = buildCharsByOffset(parent);
|
|
73
|
+
var chars = (0, _charsByOffset.buildCharsByOffset)(parent);
|
|
103
74
|
|
|
104
75
|
// Convert absolute doc positions to zero-based content offsets.
|
|
105
76
|
var fromIdx = from - parentStart;
|
|
@@ -8,6 +8,7 @@ exports.classifySmartChanges = void 0;
|
|
|
8
8
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
9
9
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
10
10
|
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
11
|
+
var _charsByOffset = require("../../utils/charsByOffset");
|
|
11
12
|
var _helpers = require("./helpers");
|
|
12
13
|
var _segmentText = require("./segmentText");
|
|
13
14
|
var _thresholds = require("./thresholds");
|
|
@@ -285,9 +286,9 @@ var classifyBlockGroup = function classifyBlockGroup(group, originalDoc, newDoc,
|
|
|
285
286
|
* Sentence- and paragraph-level classification for a single text-bearing block.
|
|
286
287
|
*/
|
|
287
288
|
var classifyTextblock = function classifyTextblock(blockA, blockB, changes, locale, thresholds) {
|
|
288
|
-
var charsB = (0,
|
|
289
|
+
var charsB = (0, _charsByOffset.buildCharsByOffset)(blockB.node);
|
|
289
290
|
var sentencesB = (0, _segmentText.segmentSentences)(charsB, locale);
|
|
290
|
-
var charsA = (0,
|
|
291
|
+
var charsA = (0, _charsByOffset.buildCharsByOffset)(blockA.node);
|
|
291
292
|
var sentencesA = (0, _segmentText.segmentSentences)(charsA, locale);
|
|
292
293
|
|
|
293
294
|
// Block content starts one position after the block's outer start (open token).
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.segmentWordSpans = exports.segmentSentences = exports.countWords =
|
|
6
|
+
exports.segmentWordSpans = exports.segmentSentences = exports.countWords = void 0;
|
|
7
|
+
var _charsByOffset = require("../../utils/charsByOffset");
|
|
7
8
|
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; } } }; }
|
|
8
9
|
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; } }
|
|
9
10
|
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; }
|
|
@@ -30,41 +31,9 @@ var NON_WORD_CHAR_SET = new Set(NON_WORD_CHARS.split(''));
|
|
|
30
31
|
var isWordChar = function isWordChar(ch) {
|
|
31
32
|
return !NON_WORD_CHAR_SET.has(ch);
|
|
32
33
|
};
|
|
33
|
-
// Whitespace is checked via a Set rather than a regex to avoid both the `require-unicode-regexp`
|
|
34
|
-
// lint rule and the TS1501 error the `u` flag triggers under this package's build lib target.
|
|
35
|
-
var WHITESPACE_CHAR_SET = new Set([' ', '\t', '\n', '\r', '\f', '\v', "\xA0"]);
|
|
36
|
-
var isWhitespaceChar = function isWhitespaceChar(ch) {
|
|
37
|
-
return WHITESPACE_CHAR_SET.has(ch);
|
|
38
|
-
};
|
|
39
34
|
// Sentence terminators used by the regex fallback. We intentionally keep this small and
|
|
40
35
|
// conservative — the Intl.Segmenter path is the source of truth when available.
|
|
41
36
|
var SENTENCE_TERMINATORS = new Set(['.', '!', '?', '…', '。', '!', '?']);
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Build a per-content-offset view of a textblock's characters.
|
|
45
|
-
*
|
|
46
|
-
* `chars[i]` is the character at content offset `i`, or `null` when that offset lies
|
|
47
|
-
* inside a non-text inline node (mention, date, emoji, hardBreak, …). A `null` acts as an
|
|
48
|
-
* *opaque single token*: it counts as one word and never terminates a sentence.
|
|
49
|
-
*/
|
|
50
|
-
var buildCharsByOffset = exports.buildCharsByOffset = function buildCharsByOffset(parent) {
|
|
51
|
-
var chars = Array.from({
|
|
52
|
-
length: parent.content.size
|
|
53
|
-
}, function () {
|
|
54
|
-
return null;
|
|
55
|
-
});
|
|
56
|
-
parent.content.forEach(function (child, offset) {
|
|
57
|
-
var _child$text;
|
|
58
|
-
if (!child.isText) {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
var text = (_child$text = child.text) !== null && _child$text !== void 0 ? _child$text : '';
|
|
62
|
-
for (var i = 0; i < text.length; i++) {
|
|
63
|
-
chars[offset + i] = text[i];
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
return chars;
|
|
67
|
-
};
|
|
68
37
|
var getSegmenterCtor = function getSegmenterCtor() {
|
|
69
38
|
if (typeof Intl === 'undefined') {
|
|
70
39
|
return undefined;
|
|
@@ -177,7 +146,7 @@ var segmentSentences = exports.segmentSentences = function segmentSentences(char
|
|
|
177
146
|
if (ch !== null && SENTENCE_TERMINATORS.has(ch)) {
|
|
178
147
|
// Consume trailing spaces as part of this sentence.
|
|
179
148
|
var end = i + 1;
|
|
180
|
-
while (end < chars.length && chars[end] !== null && isWhitespaceChar(chars[end])) {
|
|
149
|
+
while (end < chars.length && chars[end] !== null && (0, _charsByOffset.isWhitespaceChar)(chars[end])) {
|
|
181
150
|
end++;
|
|
182
151
|
}
|
|
183
152
|
spans.push({
|
|
@@ -4,11 +4,12 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
|
-
exports.unmountContributorTag = exports.mountContributorTag = exports.createContributorTagWidget = exports.createContributorTagHost = exports.CONTRIBUTOR_TAG_HOST_ATTRIBUTE = void 0;
|
|
7
|
+
exports.unmountContributorTag = exports.mountContributorTag = exports.isContributorTagWidgetEnabled = exports.createContributorTagWidget = exports.createContributorTagHost = exports.CONTRIBUTOR_TAG_HOST_ATTRIBUTE = void 0;
|
|
8
8
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
9
|
var _view = require("@atlaskit/editor-prosemirror/view");
|
|
10
10
|
var _fg = require("@atlaskit/platform-feature-flags/fg");
|
|
11
11
|
var _contributorTagController = require("../../ui/ContributorTag/contributorTagController");
|
|
12
|
+
var _charsByOffset = require("../utils/charsByOffset");
|
|
12
13
|
var _createAnchorDecorationWidgets = require("./createAnchorDecorationWidgets");
|
|
13
14
|
var _decorationKeys = require("./decorationKeys");
|
|
14
15
|
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; }
|
|
@@ -17,7 +18,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
17
18
|
var CONTRIBUTOR_TAG_HOST_ATTRIBUTE = exports.CONTRIBUTOR_TAG_HOST_ATTRIBUTE = 'data-contributor-tag-host';
|
|
18
19
|
|
|
19
20
|
// Contributor tags only render behind this gate — see `areAttributionColorGatesEnabled`.
|
|
20
|
-
var isContributorTagWidgetEnabled = function isContributorTagWidgetEnabled() {
|
|
21
|
+
var isContributorTagWidgetEnabled = exports.isContributorTagWidgetEnabled = function isContributorTagWidgetEnabled() {
|
|
21
22
|
return (0, _fg.fg)('confluence_ncs_step_diffing_version_history');
|
|
22
23
|
};
|
|
23
24
|
|
|
@@ -84,30 +85,55 @@ var createContributorTagHost = exports.createContributorTagHost = function creat
|
|
|
84
85
|
};
|
|
85
86
|
|
|
86
87
|
/**
|
|
87
|
-
* The
|
|
88
|
+
* The first *visible* character of the content that `[from, to)` highlights.
|
|
88
89
|
*
|
|
89
|
-
* `from` is not sufficient
|
|
90
|
-
* later block, and ProseMirror clips `Decoration.inline` to the content it can paint
|
|
91
|
-
* left at `from` would sit lines away from the highlight.
|
|
90
|
+
* `from` is not sufficient, for two reasons. A range can begin at the tail of one paragraph and only
|
|
91
|
+
* cover text in a later block, and ProseMirror clips `Decoration.inline` to the content it can paint
|
|
92
|
+
* — so a host left at `from` would sit lines away from the highlight. And a range routinely starts on
|
|
93
|
+
* whitespace, because the changeset joins neighbouring edits across the space between them: a tag
|
|
94
|
+
* hosted there captions the gap in front of the change rather than the change (EDITOR-8855).
|
|
95
|
+
*
|
|
96
|
+
* Whitespace is skipped across blocks, so a range whose first block contributes nothing but spaces
|
|
97
|
+
* anchors on the next block that has something to show. The fallback to the first paintable position
|
|
98
|
+
* covers a range holding no text at all — an atomic block such as media or a rule; a range of
|
|
99
|
+
* nothing but whitespace never reaches here, because a change with no visible content carries no tag
|
|
100
|
+
* (see `hasVisibleContent`).
|
|
92
101
|
*/
|
|
93
|
-
var
|
|
102
|
+
var resolveTagAnchorPos = function resolveTagAnchorPos(doc, from, to) {
|
|
103
|
+
var _ref2;
|
|
94
104
|
var edge;
|
|
105
|
+
var visibleEdge;
|
|
95
106
|
doc.nodesBetween(from, to, function (node, nodePos) {
|
|
107
|
+
if (visibleEdge !== undefined) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
96
110
|
if (!node.isTextblock) {
|
|
97
|
-
return
|
|
111
|
+
return true;
|
|
98
112
|
}
|
|
99
113
|
|
|
100
114
|
// The slice of this block the range actually covers; empty means nothing paintable.
|
|
101
|
-
var
|
|
102
|
-
var
|
|
115
|
+
var blockStart = nodePos + 1;
|
|
116
|
+
var contentFrom = Math.max(blockStart, from);
|
|
117
|
+
var contentTo = Math.min(blockStart + node.content.size, to);
|
|
103
118
|
if (contentTo > contentFrom && edge === undefined) {
|
|
104
119
|
edge = contentFrom;
|
|
105
120
|
}
|
|
106
121
|
|
|
122
|
+
// A `null` is a non-text inline node (mention, emoji, date, …). Those are visible, so they
|
|
123
|
+
// anchor the tag exactly as a letter does.
|
|
124
|
+
var chars = (0, _charsByOffset.buildCharsByOffset)(node);
|
|
125
|
+
for (var offset = contentFrom - blockStart; offset < contentTo - blockStart; offset++) {
|
|
126
|
+
var char = chars[offset];
|
|
127
|
+
if (char === null || !(0, _charsByOffset.isWhitespaceChar)(char)) {
|
|
128
|
+
visibleEdge = blockStart + offset;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
107
133
|
// Textblocks have no block children to descend into.
|
|
108
134
|
return false;
|
|
109
135
|
});
|
|
110
|
-
return
|
|
136
|
+
return (_ref2 = visibleEdge !== null && visibleEdge !== void 0 ? visibleEdge : edge) !== null && _ref2 !== void 0 ? _ref2 : from;
|
|
111
137
|
};
|
|
112
138
|
|
|
113
139
|
/**
|
|
@@ -128,21 +154,21 @@ var resolveCodeBlockStart = function resolveCodeBlockStart(doc, pos) {
|
|
|
128
154
|
};
|
|
129
155
|
|
|
130
156
|
/**
|
|
131
|
-
* A widget hosting the contributor tag of one diff range, placed on the first character the
|
|
132
|
-
* highlights — or, with `anchorAtRangeStart`, at `from` itself. Either way a host that would
|
|
133
|
-
* inside a code block is hoisted out in front of it; see `resolveCodeBlockStart`.
|
|
157
|
+
* A widget hosting the contributor tag of one diff range, placed on the first visible character the
|
|
158
|
+
* range highlights — or, with `anchorAtRangeStart`, at `from` itself. Either way a host that would
|
|
159
|
+
* land inside a code block is hoisted out in front of it; see `resolveCodeBlockStart`.
|
|
134
160
|
*
|
|
135
161
|
* The tag is mounted in `toDOM` and taken down in `destroy`, so it lives exactly as long as the host
|
|
136
162
|
* element ProseMirror drew it into — see `mountContributorTag`.
|
|
137
163
|
*/
|
|
138
|
-
var createContributorTagWidget = exports.createContributorTagWidget = function createContributorTagWidget(
|
|
139
|
-
var
|
|
140
|
-
anchorAtRangeStart =
|
|
141
|
-
doc =
|
|
142
|
-
from =
|
|
143
|
-
to =
|
|
144
|
-
diffId =
|
|
145
|
-
mountContext =
|
|
164
|
+
var createContributorTagWidget = exports.createContributorTagWidget = function createContributorTagWidget(_ref3) {
|
|
165
|
+
var _ref3$anchorAtRangeSt = _ref3.anchorAtRangeStart,
|
|
166
|
+
anchorAtRangeStart = _ref3$anchorAtRangeSt === void 0 ? false : _ref3$anchorAtRangeSt,
|
|
167
|
+
doc = _ref3.doc,
|
|
168
|
+
from = _ref3.from,
|
|
169
|
+
to = _ref3.to,
|
|
170
|
+
diffId = _ref3.diffId,
|
|
171
|
+
mountContext = _ref3.mountContext;
|
|
146
172
|
if (!isContributorTagWidgetEnabled()) {
|
|
147
173
|
return undefined;
|
|
148
174
|
}
|
|
@@ -150,7 +176,7 @@ var createContributorTagWidget = exports.createContributorTagWidget = function c
|
|
|
150
176
|
// Reassigned when ProseMirror redraws this decoration, which it may do more than once for the
|
|
151
177
|
// same `Decoration` instance.
|
|
152
178
|
var mount;
|
|
153
|
-
var anchorPos = anchorAtRangeStart ? from :
|
|
179
|
+
var anchorPos = anchorAtRangeStart ? from : resolveTagAnchorPos(doc, from, to);
|
|
154
180
|
// A change inside a code block is hoisted out of it; anything else keeps its own anchor.
|
|
155
181
|
var codeBlockPos = resolveCodeBlockStart(doc, anchorPos);
|
|
156
182
|
return _view.Decoration.widget(
|
|
@@ -11,6 +11,7 @@ var _lazyNodeView = require("@atlaskit/editor-common/lazy-node-view");
|
|
|
11
11
|
var _view = require("@atlaskit/editor-prosemirror/view");
|
|
12
12
|
var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
|
|
13
13
|
var _isExtendedEnabled = require("../isExtendedEnabled");
|
|
14
|
+
var _hasVisibleContent = require("../utils/hasVisibleContent");
|
|
14
15
|
var _factory = require("./colorSchemes/factory");
|
|
15
16
|
var _schemes = require("./colorSchemes/schemes");
|
|
16
17
|
var _createAnchorDecorationWidgets = require("./createAnchorDecorationWidgets");
|
|
@@ -141,6 +142,12 @@ var createInlineChangedDecoration = exports.createInlineChangedDecoration = func
|
|
|
141
142
|
// rebuilds decorations without touching the doc, and a fresh id would change the React key and the
|
|
142
143
|
// tag host's key, remounting every tag mid-step. Random ids are kept when tags are off.
|
|
143
144
|
var diffId = showContributorTags ? "inline-".concat(change.fromB, "-").concat(change.toB) : crypto.randomUUID();
|
|
145
|
+
|
|
146
|
+
// Whether this highlight hosts a tag. A range of nothing but whitespace has nothing to caption,
|
|
147
|
+
// so it stays unattributed: the spec is the only route attribution takes to
|
|
148
|
+
// `extractContributorTags`, so dropping the key there is what keeps a tag from being resolved for
|
|
149
|
+
// a host that is never emitted (EDITOR-8855).
|
|
150
|
+
var canTagChange = showContributorTags && !!doc && (0, _isExtendedEnabled.isExtendedEnabled)(diffType) && (0, _createContributorTagWidget.isContributorTagWidgetEnabled)() && (0, _hasVisibleContent.hasVisibleContent)(doc.slice(change.fromB, change.toB).content);
|
|
144
151
|
if (shouldHideDeleted) {
|
|
145
152
|
return [_view.Decoration.inline(change.fromB, change.toB, {
|
|
146
153
|
style: displayNoneStyle
|
|
@@ -185,7 +192,7 @@ var createInlineChangedDecoration = exports.createInlineChangedDecoration = func
|
|
|
185
192
|
}, showContributorTags && {
|
|
186
193
|
'data-diff-id': diffId
|
|
187
194
|
}), (0, _decorationKeys.buildDiffDecorationSpec)({
|
|
188
|
-
attributionKey: attributionKey,
|
|
195
|
+
attributionKey: canTagChange ? attributionKey : undefined,
|
|
189
196
|
colorScheme: colorScheme,
|
|
190
197
|
decorationType: 'inline',
|
|
191
198
|
diffId: diffId,
|
|
@@ -230,7 +237,7 @@ var createInlineChangedDecoration = exports.createInlineChangedDecoration = func
|
|
|
230
237
|
|
|
231
238
|
// Built from the raw change range, not the anchors above: a tag lines up with the changed text,
|
|
232
239
|
// while the bar's anchors are held on a block boundary when a deleted widget renders beside them.
|
|
233
|
-
if (
|
|
240
|
+
if (canTagChange && doc) {
|
|
234
241
|
var tagWidget = (0, _createContributorTagWidget.createContributorTagWidget)({
|
|
235
242
|
doc: doc,
|
|
236
243
|
from: change.fromB,
|
|
@@ -9,6 +9,7 @@ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/de
|
|
|
9
9
|
var _view = require("@atlaskit/editor-prosemirror/view");
|
|
10
10
|
var _fg = require("@atlaskit/platform-feature-flags/fg");
|
|
11
11
|
var _isExtendedEnabled = require("../isExtendedEnabled");
|
|
12
|
+
var _hasVisibleContent = require("../utils/hasVisibleContent");
|
|
12
13
|
var _createAnchorDecorationWidgets = require("./createAnchorDecorationWidgets");
|
|
13
14
|
var _createChangedRowDecorationWidgets = require("./createChangedRowDecorationWidgets");
|
|
14
15
|
var _createContributorTagWidget = require("./createContributorTagWidget");
|
|
@@ -220,6 +221,10 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
|
|
|
220
221
|
// Derived from the deleted range so it survives a recalculation — see the same reasoning in
|
|
221
222
|
// `createInlineChangedDecoration`.
|
|
222
223
|
var diffId = showContributorTags ? "widget-".concat(change.fromA, "-").concat(change.toA) : crypto.randomUUID();
|
|
224
|
+
// Whether this widget hosts a tag. Removing nothing but whitespace leaves no visible deleted
|
|
225
|
+
// content to caption, so the change stays unattributed — matching the inline half's rule in
|
|
226
|
+
// `createInlineChangedDecoration` (EDITOR-8855).
|
|
227
|
+
var canTagWidget = showContributorTags && (0, _hasVisibleContent.hasVisibleContent)(slice.content);
|
|
223
228
|
var decorations = [];
|
|
224
229
|
var replacementNode = newDoc.nodeAt(change.fromB);
|
|
225
230
|
var firstReplacedNode = slice.content.firstChild;
|
|
@@ -355,7 +360,7 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
|
|
|
355
360
|
// Taken down in the widget's `destroy` below: `dom` is rebuilt on every recalculation, so the tag
|
|
356
361
|
// mounted into it must not outlive the copy ProseMirror is drawing.
|
|
357
362
|
var tagMount;
|
|
358
|
-
if (
|
|
363
|
+
if (canTagWidget) {
|
|
359
364
|
// Lets the contributor tag find the deleted content on hover.
|
|
360
365
|
dom.setAttribute('data-diff-id', diffId);
|
|
361
366
|
|
|
@@ -378,7 +383,7 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
|
|
|
378
383
|
}
|
|
379
384
|
}
|
|
380
385
|
decorations.push(_view.Decoration.widget(safeInsertPos, dom, _objectSpread(_objectSpread(_objectSpread({}, (0, _decorationKeys.buildDiffDecorationSpec)(_objectSpread({
|
|
381
|
-
attributionKey: attributionKey,
|
|
386
|
+
attributionKey: canTagWidget ? attributionKey : undefined,
|
|
382
387
|
colorScheme: colorScheme,
|
|
383
388
|
decorationType: 'widget',
|
|
384
389
|
diffId: diffId,
|
|
@@ -8,6 +8,7 @@ exports.extractContributorTags = void 0;
|
|
|
8
8
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
9
|
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
10
10
|
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
|
|
11
|
+
var _fg = require("@atlaskit/platform-feature-flags/fg");
|
|
11
12
|
var _decorationKeys = require("./decorationKeys");
|
|
12
13
|
var _excluded = ["connectedToKey"];
|
|
13
14
|
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; }
|
|
@@ -36,6 +37,16 @@ var isSameChange = function isSameChange(widget, inline) {
|
|
|
36
37
|
return widget.spec.attributionKey === inline.spec.attributionKey && widget.from >= inline.from && widget.from <= inline.to;
|
|
37
38
|
};
|
|
38
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Whether a deleted-content widget is the left-most half of the replacement it belongs to, in which
|
|
42
|
+
* case it keeps the tag so the tag marks where the change begins (EDITOR-8855). `side: -1` paints
|
|
43
|
+
* the deleted content in front of the inserted text; `1` is `deletedDiffPlacement: 'bottom'` /
|
|
44
|
+
* `inlineDeletedDiffPlacement: 'after'`, where the inline half leads instead.
|
|
45
|
+
*/
|
|
46
|
+
var leadsReplacement = function leadsReplacement(widget) {
|
|
47
|
+
return (0, _fg.fg)('confluence_ncs_step_diffing_version_history') && widget.spec.side === -1;
|
|
48
|
+
};
|
|
49
|
+
|
|
39
50
|
/**
|
|
40
51
|
* Whether `inner` captions the same change as the block decoration `block`, in which case only the
|
|
41
52
|
* block keeps the tag — one tag per block. Containment counts in either direction: a leaf block's
|
|
@@ -48,7 +59,7 @@ var isSameBlockChange = function isSameBlockChange(inner, block) {
|
|
|
48
59
|
/**
|
|
49
60
|
* One model per diff decoration whose attribution resolves to a supplied contributor; anything
|
|
50
61
|
* unattributed, untaggable or unresolved is skipped. A replacement's two decorations are collapsed
|
|
51
|
-
* into
|
|
62
|
+
* into a single tag, on whichever half renders first — see `isSameChange` and `leadsReplacement`.
|
|
52
63
|
*/
|
|
53
64
|
var extractContributorTags = exports.extractContributorTags = function extractContributorTags(decorations, contributors) {
|
|
54
65
|
if (!contributors) {
|
|
@@ -77,7 +88,7 @@ var extractContributorTags = exports.extractContributorTags = function extractCo
|
|
|
77
88
|
});
|
|
78
89
|
}
|
|
79
90
|
|
|
80
|
-
//
|
|
91
|
+
// Candidate hosts, in the order they outrank each other: block, then leading widget, then inline.
|
|
81
92
|
} catch (err) {
|
|
82
93
|
_iterator.e(err);
|
|
83
94
|
} finally {
|
|
@@ -91,6 +102,10 @@ var extractContributorTags = exports.extractContributorTags = function extractCo
|
|
|
91
102
|
var decoration = _ref3.decoration;
|
|
92
103
|
return decoration.spec.decorationType === 'block';
|
|
93
104
|
});
|
|
105
|
+
var widgetTargets = targets.filter(function (_ref4) {
|
|
106
|
+
var decoration = _ref4.decoration;
|
|
107
|
+
return decoration.spec.decorationType === 'widget';
|
|
108
|
+
});
|
|
94
109
|
var linkedDiffIds = new Map();
|
|
95
110
|
var folded = new Set();
|
|
96
111
|
var fold = function fold(host, target) {
|
|
@@ -107,19 +122,32 @@ var extractContributorTags = exports.extractContributorTags = function extractCo
|
|
|
107
122
|
}
|
|
108
123
|
|
|
109
124
|
// A block outranks both other kinds, so it is tried first.
|
|
110
|
-
var block = blockTargets.find(function (
|
|
111
|
-
var decoration =
|
|
125
|
+
var block = blockTargets.find(function (_ref5) {
|
|
126
|
+
var decoration = _ref5.decoration;
|
|
112
127
|
return isSameBlockChange(target.decoration, decoration);
|
|
113
128
|
});
|
|
114
129
|
if (block) {
|
|
115
130
|
fold(block, target);
|
|
116
131
|
return 0; // continue
|
|
117
132
|
}
|
|
118
|
-
if (decorationType
|
|
133
|
+
if (decorationType === 'inline') {
|
|
134
|
+
// The deleted half renders in front of this one, so that half captions the change.
|
|
135
|
+
var leadingWidget = widgetTargets.find(function (_ref6) {
|
|
136
|
+
var decoration = _ref6.decoration;
|
|
137
|
+
return leadsReplacement(decoration) && isSameChange(decoration, target.decoration);
|
|
138
|
+
});
|
|
139
|
+
if (leadingWidget) {
|
|
140
|
+
fold(leadingWidget, target);
|
|
141
|
+
}
|
|
119
142
|
return 0; // continue
|
|
120
143
|
}
|
|
121
|
-
|
|
122
|
-
|
|
144
|
+
|
|
145
|
+
// A leading widget keeps its own tag; it was folded into above.
|
|
146
|
+
if (leadsReplacement(target.decoration)) {
|
|
147
|
+
return 0; // continue
|
|
148
|
+
}
|
|
149
|
+
var host = inlineTargets.find(function (_ref7) {
|
|
150
|
+
var decoration = _ref7.decoration;
|
|
123
151
|
return isSameChange(target.decoration, decoration);
|
|
124
152
|
});
|
|
125
153
|
if (host) {
|
|
@@ -133,9 +161,9 @@ var extractContributorTags = exports.extractContributorTags = function extractCo
|
|
|
133
161
|
}
|
|
134
162
|
return targets.filter(function (target) {
|
|
135
163
|
return !folded.has(target);
|
|
136
|
-
}).map(function (
|
|
137
|
-
var contributor =
|
|
138
|
-
spec =
|
|
164
|
+
}).map(function (_ref8) {
|
|
165
|
+
var contributor = _ref8.contributor,
|
|
166
|
+
spec = _ref8.decoration.spec;
|
|
139
167
|
var connected = contributor.connectedToKey ? contributors[contributor.connectedToKey] : undefined;
|
|
140
168
|
var connectedContributor = connected ? toTagContributor(connected) : undefined;
|
|
141
169
|
var linked = linkedDiffIds.get(spec.diffId);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isWhitespaceChar = exports.buildCharsByOffset = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Whitespace as the diff treats it, U+00A0 included: a non-breaking space separates words for every
|
|
9
|
+
* consumer here, so it is not a character a change can be said to start on.
|
|
10
|
+
*
|
|
11
|
+
* A Set rather than a regex, to avoid both the `require-unicode-regexp` lint rule and the TS1501
|
|
12
|
+
* error the `u` flag triggers under this package's declaration build target.
|
|
13
|
+
*/
|
|
14
|
+
var WHITESPACE_CHAR_SET = new Set([' ', '\t', '\n', '\r', '\f', '\v', "\xA0"]);
|
|
15
|
+
var isWhitespaceChar = exports.isWhitespaceChar = function isWhitespaceChar(char) {
|
|
16
|
+
return WHITESPACE_CHAR_SET.has(char);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Build a per-content-offset view of a textblock's characters.
|
|
21
|
+
*
|
|
22
|
+
* Returns an array whose length is `parent.content.size`. For every offset that lies inside a text
|
|
23
|
+
* node, `chars[offset]` is the character at that offset; for every offset that lies inside (or on
|
|
24
|
+
* the edge of) a non-text inline node — hardBreak, mention, emoji, date, … — the entry is `null`,
|
|
25
|
+
* which acts as an *opaque single token*: it counts as one word and is never whitespace.
|
|
26
|
+
*
|
|
27
|
+
* Using doc positions to index `parent.textContent` is wrong because `textContent` strips non-text
|
|
28
|
+
* inline nodes, so every such node shifts the lookup off by its size. This per-offset view restores
|
|
29
|
+
* a 1:1 mapping between doc positions inside the textblock and the character (or "no character",
|
|
30
|
+
* i.e. a hard word boundary) at that position.
|
|
31
|
+
*/
|
|
32
|
+
var buildCharsByOffset = exports.buildCharsByOffset = function buildCharsByOffset(parent) {
|
|
33
|
+
var chars = new Array(parent.content.size).fill(null);
|
|
34
|
+
parent.content.forEach(function (child, offset) {
|
|
35
|
+
var _child$text;
|
|
36
|
+
if (!child.isText) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
var text = (_child$text = child.text) !== null && _child$text !== void 0 ? _child$text : '';
|
|
40
|
+
for (var i = 0; i < text.length; i++) {
|
|
41
|
+
chars[offset + i] = text[i];
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
return chars;
|
|
45
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.hasVisibleContent = void 0;
|
|
8
|
+
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
9
|
+
var _charsByOffset = require("./charsByOffset");
|
|
10
|
+
/**
|
|
11
|
+
* Whether `fragment` holds anything a reader can see.
|
|
12
|
+
*
|
|
13
|
+
* Text counts once it carries a single non-whitespace character; a leaf node (mention, emoji, date,
|
|
14
|
+
* media, rule, hardBreak, …) counts in its own right, exactly as `resolveTagAnchorPos` treats one.
|
|
15
|
+
* Everything else is descended into, so a paragraph is visible when its content is.
|
|
16
|
+
*
|
|
17
|
+
* A change whose content is nothing but whitespace — a space typed between two words, a run of
|
|
18
|
+
* trailing spaces, an added blank paragraph — has no visible content for a contributor tag to
|
|
19
|
+
* caption, so it carries no tag (EDITOR-8855).
|
|
20
|
+
*/
|
|
21
|
+
var _hasVisibleContent = exports.hasVisibleContent = function hasVisibleContent(fragment) {
|
|
22
|
+
var visible = false;
|
|
23
|
+
fragment.forEach(function (node) {
|
|
24
|
+
if (visible) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (node.isText) {
|
|
28
|
+
var _node$text;
|
|
29
|
+
visible = (0, _toConsumableArray2.default)((_node$text = node.text) !== null && _node$text !== void 0 ? _node$text : '').some(function (char) {
|
|
30
|
+
return !(0, _charsByOffset.isWhitespaceChar)(char);
|
|
31
|
+
});
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
visible = node.isLeaf || _hasVisibleContent(node.content);
|
|
35
|
+
});
|
|
36
|
+
return visible;
|
|
37
|
+
};
|
|
@@ -94,7 +94,7 @@ var identityOf = function identityOf(model) {
|
|
|
94
94
|
* `toDOM` for a widget it reuses: stepping to the next change republishes the tags without redrawing
|
|
95
95
|
* their hosts, so anything the tag reacts to — `isActive` above all — has to arrive through a
|
|
96
96
|
* subscription of its own (EDITOR-8702). Draws nothing when this diff has no tag, which is the case
|
|
97
|
-
* for the
|
|
97
|
+
* for the folded half of a replacement: its two decorations share the one tag.
|
|
98
98
|
*/
|
|
99
99
|
var ContributorTagController = exports.ContributorTagController = /*#__PURE__*/function () {
|
|
100
100
|
function ContributorTagController(options) {
|
|
@@ -2,6 +2,7 @@ import { simplifyChanges, ChangeSet } from 'prosemirror-changeset';
|
|
|
2
2
|
import { Mark } from '@atlaskit/editor-prosemirror/model';
|
|
3
3
|
import { Mapping, ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
|
|
4
4
|
import { getAttributionKey } from '../decorations/colorSchemes/attributions';
|
|
5
|
+
import { buildCharsByOffset } from '../utils/charsByOffset';
|
|
5
6
|
import { attrAwareTokenEncoder } from './attrAwareTokenEncoder';
|
|
6
7
|
import { optimizeChanges } from './optimizeChanges';
|
|
7
8
|
const WORD_CHAR_REGEX = /[\p{L}\p{N}_]/u;
|
|
@@ -9,36 +10,6 @@ const PUNCTUATION_REGEX = /\p{P}/u;
|
|
|
9
10
|
const WHITESPACE_REGEX = /\s/u;
|
|
10
11
|
const mapPosition = (mapping, pos) => mapping.map(pos);
|
|
11
12
|
|
|
12
|
-
/**
|
|
13
|
-
* Build a per-content-offset view of the textblock's characters.
|
|
14
|
-
*
|
|
15
|
-
* Returns an array `chars` whose length is `parent.content.size`. For every
|
|
16
|
-
* offset that lies inside a text node, `chars[offset]` is the character at
|
|
17
|
-
* that offset; for every offset that lies inside (or on the edge of) a
|
|
18
|
-
* non-text inline node — hardBreak, mention, emoji, date, etc. — the entry
|
|
19
|
-
* is `null`.
|
|
20
|
-
*
|
|
21
|
-
* Using doc positions to index `parent.textContent` is wrong because
|
|
22
|
-
* `textContent` strips non-text inline nodes, so every such node shifts the
|
|
23
|
-
* lookup off by its size. This per-offset view restores a 1:1 mapping between
|
|
24
|
-
* doc positions inside the textblock and the character (or "no character",
|
|
25
|
-
* i.e. a hard word boundary) at that position.
|
|
26
|
-
*/
|
|
27
|
-
const buildCharsByOffset = parent => {
|
|
28
|
-
const chars = new Array(parent.content.size).fill(null);
|
|
29
|
-
parent.content.forEach((child, offset) => {
|
|
30
|
-
var _child$text;
|
|
31
|
-
if (!child.isText) {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
const text = (_child$text = child.text) !== null && _child$text !== void 0 ? _child$text : '';
|
|
35
|
-
for (let i = 0; i < text.length; i++) {
|
|
36
|
-
chars[offset + i] = text[i];
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
return chars;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
13
|
/**
|
|
43
14
|
* Given a ProseMirror doc and a position range [from, to], expand
|
|
44
15
|
* both endpoints outward to the nearest word boundaries.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { buildCharsByOffset } from '../../utils/charsByOffset';
|
|
1
2
|
import { makePromotedChange, mergeOverlappingByNewDocRange, rangesOverlap } from './helpers';
|
|
2
|
-
import {
|
|
3
|
+
import { countWords, segmentSentences, segmentWordSpans } from './segmentText';
|
|
3
4
|
import { resolveThresholds } from './thresholds';
|
|
4
5
|
/**
|
|
5
6
|
* Block-first `smart` classifier.
|