@atlaskit/editor-common
Version:
A package that contains common classes and components for editor and renderer
74 lines • 2.67 kB
JavaScript
import { Fragment } from '@atlaskit/editor-prosemirror/model';
/**
* Narrows a full-list replacement to the minimal changed range.
*
* Compares the old root list node with the new replacement fragment
* from both ends to find the first and last positions where they differ,
* then returns only the changed subrange.
*
* This reduces the scope of `tr.replaceWith()` so that remote cursors
* on unchanged items are preserved during collaborative editing.
*/
export function narrowReplacementRange(doc, rootListStart, rootListEnd, fragment, contentStartOffsets) {
const oldNode = doc.nodeAt(rootListStart);
const newNode = fragment.childCount === 1 ? fragment.firstChild : null;
if (!oldNode || !newNode || newNode.type !== oldNode.type) {
return {
start: rootListStart,
end: rootListEnd,
fragment,
adjustedContentStartOffsets: contentStartOffsets
};
}
const minChildCount = Math.min(oldNode.childCount, newNode.childCount);
let commonPrefixChildren = 0;
let prefixSize = 0;
for (let i = 0; i < minChildCount; i++) {
const oldChild = oldNode.child(i);
const newChild = newNode.child(i);
if (oldChild.eq(newChild)) {
commonPrefixChildren++;
prefixSize += oldChild.nodeSize;
} else {
break;
}
}
let commonSuffixChildren = 0;
let suffixSize = 0;
for (let i = 0; i < minChildCount - commonPrefixChildren; i++) {
const oldChild = oldNode.child(oldNode.childCount - 1 - i);
const newChild = newNode.child(newNode.childCount - 1 - i);
if (oldChild.eq(newChild)) {
commonSuffixChildren++;
suffixSize += oldChild.nodeSize;
} else {
break;
}
}
const totalCommon = commonPrefixChildren + commonSuffixChildren;
if (totalCommon >= oldNode.childCount && totalCommon >= newNode.childCount) {
return {
start: rootListStart,
end: rootListStart,
fragment: Fragment.empty,
adjustedContentStartOffsets: contentStartOffsets
};
}
const narrowedStart = rootListStart + 1 + prefixSize;
const narrowedEnd = rootListEnd - 1 - suffixSize;
const changedChildStart = commonPrefixChildren;
const changedChildEnd = newNode.childCount - commonSuffixChildren;
const changedNodes = [];
for (let i = changedChildStart; i < changedChildEnd; i++) {
changedNodes.push(newNode.child(i));
}
const narrowedFragment = Fragment.from(changedNodes);
const prefixOffset = 1 + prefixSize;
const adjustedContentStartOffsets = contentStartOffsets.map(offset => offset - prefixOffset);
return {
start: narrowedStart,
end: narrowedEnd,
fragment: narrowedFragment,
adjustedContentStartOffsets
};
}