Add cloneNotNeeded Set, remove depth from dirtySubTrees (#844)

* Add cloneNotNeeded Set, remove depth from dirtySubTrees

* Fix codes

* Fix codes
This commit is contained in:
Dominic Gannaway
2021-11-17 17:01:31 +00:00
committed by acywatson
parent 031981a282
commit c3115fc213
7 changed files with 74 additions and 84 deletions

View File

@ -111,6 +111,7 @@ function textTransform(node: TextNode, state: State): void {
}
function traverseNodes(node: BlockNode): void {
debugger
let child = node.getFirstChild();
while (child !== null) {

View File

@ -144,8 +144,9 @@ export function resetEditor(
editor._pendingEditorState = pendingEditorState;
editor._compositionKey = null;
editor._dirtyType = NO_DIRTY_NODES;
editor._cloneNotNeeded.clear();
editor._dirtyNodes = new Set();
editor._dirtyBlocks = new Map();
editor._dirtySubTrees.clear();
editor._log = [];
editor._updates = [];
const observer = editor._observer;
@ -207,8 +208,9 @@ class BaseOutlineEditor {
_textContent: string;
_config: EditorConfig<{...}>;
_dirtyType: 0 | 1 | 2;
_cloneNotNeeded: Set<NodeKey>;
_dirtyNodes: Set<NodeKey>;
_dirtyBlocks: Map<NodeKey, Number>;
_dirtySubTrees: Set<NodeKey>;
_observer: null | MutationObserver;
_log: Array<string>;
@ -254,8 +256,9 @@ class BaseOutlineEditor {
this._pendingDecorators = null;
// Used to optimize reconcilation
this._dirtyType = NO_DIRTY_NODES;
this._cloneNotNeeded = new Set();
this._dirtyNodes = new Set();
this._dirtyBlocks = new Map();
this._dirtySubTrees = new Set();
// Handling of DOM mutations
this._observer = null;
// Logging for updates
@ -430,8 +433,9 @@ declare export class OutlineEditor {
_pendingDecorators: null | {[NodeKey]: ReactNode};
_config: EditorConfig<{...}>;
_dirtyType: 0 | 1 | 2;
_cloneNotNeeded: Set<NodeKey>;
_dirtyNodes: Set<NodeKey>;
_dirtyBlocks: Map<NodeKey, number>;
_dirtySubTrees: Set<NodeKey>;
_observer: null | MutationObserver;
_log: Array<string>;

View File

@ -24,7 +24,6 @@ import {
internallyMarkNodeAsDirty,
markParentBlocksAsDirty,
setCompositionKey,
getBlockDepth,
} from './OutlineUtils';
import invariant from 'shared/invariant';
import {
@ -510,15 +509,12 @@ export class OutlineNode {
// Ensure we get the latest node from pending state
const latestNode = this.getLatest();
const parent = latestNode.__parent;
const dirtyBlocks = editor._dirtyBlocks;
const dirtySubTrees = editor._dirtySubTrees;
if (parent !== null) {
markParentBlocksAsDirty(parent, nodeMap, dirtyBlocks);
}
const dirtyNodes = editor._dirtyNodes;
if (dirtyNodes.has(key)) {
if (isBlockNode(this)) {
dirtyBlocks.set(key, getBlockDepth(this));
markParentBlocksAsDirty(parent, nodeMap, dirtySubTrees);
}
const cloneNotNeeded = editor._cloneNotNeeded;
if (cloneNotNeeded.has(key)) {
return latestNode;
}
const constructor = latestNode.constructor;
@ -533,6 +529,7 @@ export class OutlineNode {
mutableNode.__format = latestNode.__format;
mutableNode.__style = latestNode.__style;
}
cloneNotNeeded.add(key);
mutableNode.__key = key;
internallyMarkNodeAsDirty(mutableNode);
// Update reference in node map

View File

@ -47,7 +47,7 @@ let editorTextContent = '';
let activeEditorConfig: EditorConfig<{...}>;
let activeEditor: OutlineEditor;
let treatAllNodesAsDirty: boolean = false;
let activeDirtyBlocks: Map<NodeKey, number>;
let activeDirtySubTrees: Set<NodeKey>;
let activeDirtyNodes: Set<NodeKey>;
let activePrevNodeMap: NodeMap;
let activeNextNodeMap: NodeMap;
@ -331,7 +331,7 @@ function reconcileNode(
const isDirty =
treatAllNodesAsDirty ||
activeDirtyNodes.has(key) ||
activeDirtyBlocks.has(key);
activeDirtySubTrees.has(key);
const dom = getElementByKeyOrThrow(activeEditor, key);
if (prevNode === nextNode && !isDirty) {
@ -526,7 +526,7 @@ function reconcileRoot(
editor: OutlineEditor,
selection: null | OutlineSelection,
dirtyType: 0 | 1 | 2,
dirtyBlocks: Map<NodeKey, number>,
dirtySubTrees: Set<NodeKey>,
dirtyNodes: Set<NodeKey>,
): void {
subTreeTextContent = '';
@ -536,7 +536,7 @@ function reconcileRoot(
treatAllNodesAsDirty = dirtyType === FULL_RECONCILE;
activeEditor = editor;
activeEditorConfig = editor._config;
activeDirtyBlocks = dirtyBlocks;
activeDirtySubTrees = dirtySubTrees;
activeDirtyNodes = dirtyNodes;
activePrevNodeMap = prevEditorState._nodeMap;
activeNextNodeMap = nextEditorState._nodeMap;
@ -551,7 +551,7 @@ function reconcileRoot(
// $FlowFixMe
activeEditor = undefined;
// $FlowFixMe
activeDirtyBlocks = undefined;
activeDirtySubTrees = undefined;
// $FlowFixMe
activeDirtyNodes = undefined;
// $FlowFixMe
@ -579,7 +579,7 @@ export function updateEditorState(
if (needsUpdate && observer !== null) {
const dirtyType = editor._dirtyType;
const dirtyBlocks = editor._dirtyBlocks;
const dirtySubTrees = editor._dirtySubTrees;
const dirtyNodes = editor._dirtyNodes;
observer.disconnect();
@ -590,7 +590,7 @@ export function updateEditorState(
editor,
pendingSelection,
dirtyType,
dirtyBlocks,
dirtySubTrees,
dirtyNodes,
);
} finally {

View File

@ -166,7 +166,7 @@ function isNodeValidForTransform(
function applyAllTransforms(
editorState: EditorState,
dirtyNodes: Set<NodeKey>,
dirtyBlocks: Map<NodeKey, number>,
dirtySubTrees: Set<NodeKey>,
editor: OutlineEditor,
): void {
const transforms = editor._transforms;
@ -207,15 +207,13 @@ function applyAllTransforms(
}
}
if (blockTransforms.size > 0 || rootTransforms.size > 0) {
const dirtyNodesArr = Array.from(dirtyBlocks);
const dirtyNodesArr = Array.from(dirtySubTrees);
const blockTransformsArr = Array.from(blockTransforms);
const rootTransformsArr = Array.from(rootTransforms);
const blockTransformsArrLength = blockTransformsArr.length;
const rootTransformsArrLength = rootTransformsArr.length;
// Sort the blocks by their depth, so we deal with deepest first
dirtyNodesArr.sort((a, b) => b[1] - a[1]);
for (let s = 0; s < dirtyNodesArr.length; s++) {
const nodeKey = dirtyNodesArr[s][0];
const nodeKey = dirtyNodesArr[s];
const node = nodeMap.get(nodeKey);
if (isNodeValidForTransform(node, compositionKey)) {
@ -383,8 +381,9 @@ export function commitPendingUpdates(editor: OutlineEditor): void {
editor._log = [];
if (needsUpdate) {
editor._dirtyType = NO_DIRTY_NODES;
editor._cloneNotNeeded.clear();
editor._dirtyNodes = new Set();
editor._dirtyBlocks = new Map();
editor._dirtySubTrees.clear();
}
garbageCollectDetachedDecorators(editor, pendingEditorState);
const pendingDecorators = editor._pendingDecorators;
@ -514,14 +513,14 @@ export function beginUpdate(
applySelectionTransforms(pendingEditorState, editor);
if (editor._dirtyType !== NO_DIRTY_NODES) {
const dirtyNodes = editor._dirtyNodes;
const dirtyBlocks = editor._dirtyBlocks;
const dirtySubTrees = editor._dirtySubTrees;
if (pendingEditorState.isEmpty()) {
invariant(
false,
'updateEditor: the pending editor state is empty. Ensure the root not never becomes empty from an update.',
);
}
applyAllTransforms(pendingEditorState, dirtyNodes, dirtyBlocks, editor);
applyAllTransforms(pendingEditorState, dirtyNodes, dirtySubTrees, editor);
processNestedUpdates(editor, deferred);
garbageCollectDetachedNodes(
currentEditorState,
@ -556,8 +555,9 @@ export function beginUpdate(
// Restore existing editor state to the DOM
editor._pendingEditorState = currentEditorState;
editor._dirtyType = FULL_RECONCILE;
editor._cloneNotNeeded.clear();
editor._dirtyNodes = new Set();
editor._dirtyBlocks = new Map();
editor._dirtySubTrees.clear();
editor._log.push('UpdateRecover');
commitPendingUpdates(editor);
return;

View File

@ -138,6 +138,7 @@ export function generateKey(node: OutlineNode): NodeKey {
const key = generateRandomKey();
editorState._nodeMap.set(key, node);
editor._dirtyNodes.add(key);
editor._cloneNotNeeded.add(key);
editor._dirtyType = HAS_DIRTY_NODES;
return key;
}
@ -145,18 +146,18 @@ export function generateKey(node: OutlineNode): NodeKey {
export function markParentBlocksAsDirty(
parentKey: NodeKey,
nodeMap: NodeMap,
dirtyBlocks: Map<NodeKey, number>,
dirtySubTress: Set<NodeKey>,
): void {
let nextParentKey = parentKey;
while (nextParentKey !== null) {
if (dirtyBlocks.has(nextParentKey)) {
if (dirtySubTress.has(nextParentKey)) {
return;
}
const node = nodeMap.get(nextParentKey);
if (node === undefined) {
break;
}
dirtyBlocks.set(nextParentKey, getBlockDepth(node));
dirtySubTress.add(nextParentKey);
nextParentKey = node.__parent;
}
}
@ -169,17 +170,14 @@ export function internallyMarkNodeAsDirty(node: OutlineNode): void {
const editorState = getActiveEditorState();
const editor = getActiveEditor();
const nodeMap = editorState._nodeMap;
const dirtyBlocks = editor._dirtyBlocks;
const dirtySubTrees = editor._dirtySubTrees;
if (parent !== null) {
markParentBlocksAsDirty(parent, nodeMap, dirtyBlocks);
markParentBlocksAsDirty(parent, nodeMap, dirtySubTrees);
}
const dirtyNodes = editor._dirtyNodes;
const key = latest.__key;
editor._dirtyType = HAS_DIRTY_NODES;
dirtyNodes.add(latest.__key);
if (isBlockNode(node)) {
dirtyBlocks.set(key, getBlockDepth(node));
}
dirtyNodes.add(key);
}
export function setCompositionKey(compositionKey: null | NodeKey): void {
@ -253,16 +251,6 @@ export function getEditorStateTextContent(editorState: EditorState): string {
return editorState.read((view) => view.getRoot().getTextContent());
}
export function getBlockDepth(startingNode: OutlineNode): number {
let node = startingNode.getParent();
let depth = 0;
while (node !== null) {
depth++;
node = node.getParent();
}
return depth;
}
export function markAllNodesAsDirty(
editor: OutlineEditor,
type: 'text' | 'decorator' | 'block' | 'root',

View File

@ -7,41 +7,41 @@
"5": "insertNodes: cannot insert a non-block into a root node",
"6": "insertNodes: cloned parent clone is not a block",
"7": "insertText: first node is not a text node",
"8": "setEditorState: the editor state is empty. Ensure the editor state's root node never becomes empty.",
"9": "updateDOM: prevInnerDOM is null or undefined",
"10": "updateDOM: innerDOM is null or undefined",
"11": "setFormat: can only be used on non-immutable nodes",
"12": "setStyle: can only be used on non-immutable nodes",
"13": "setTextContent: can only be used on non-immutable text nodes",
"14": "spliceText: can only be used on non-immutable text nodes",
"15": "spliceText: selection not found",
"16": "splitText: can only be used on non-immutable text nodes",
"17": "select: cannot be called on root nodes",
"18": "remove: cannot be called on root nodes",
"19": "replace: cannot be called on root nodes",
"20": "insertBefore: cannot be called on root nodes",
"21": "insertAfter: cannot be called on root nodes",
"22": "rootNode.append: Only block nodes can be appended to the root node",
"23": "createNodeFromParse: type \"%s\" + not found",
"24": "Point.getNode: node not found",
"25": "decorate: base method not extended",
"26": "Cannot use method in read-only mode.",
"27": "Unable to find an active editor state. State helpers or node methods can only be used synchronously during the callback of editor.update() or editorState.read().",
"28": "Unable to find an active editor. This method can only be used synchronously during the callback of editor.update().",
"29": "updateEditor: the pending editor state is empty. Ensure the root not never becomes empty from an update.",
"30": "updateEditor: selection has been lost because the previously selected nodes have been removed and selection wasn't moved to another node. Ensure selection changes after removing/replacing a selected node.",
"31": "createNode: node does not exist in nodeMap",
"32": "reconcileNode: prevNode or nextNode does not exist in nodeMap",
"33": "reconcileNode: parentDOM is null",
"34": "Reconciliation: could not find DOM element for node key \"${key}\"",
"35": "OutlineNode: Node type %s does not implement .clone().",
"36": "Expected node %s to have a parent.",
"37": "Expected node %s to have a parent block.",
"38": "Expected node %s to have a top parent block.",
"39": "getNodesBetween: ancestor is null",
"40": "getLatest: node not found",
"41": "createDOM: base method not extended",
"42": "updateDOM: base method not extended",
"43": "setFlags: can only be used on non-immutable nodes",
"44": "Expected node with key %s to exist but it's not in the nodeMap."
"8": "decorate: base method not extended",
"9": "setEditorState: the editor state is empty. Ensure the editor state's root node never becomes empty.",
"10": "updateDOM: prevInnerDOM is null or undefined",
"11": "updateDOM: innerDOM is null or undefined",
"12": "setFormat: can only be used on non-immutable nodes",
"13": "setStyle: can only be used on non-immutable nodes",
"14": "setTextContent: can only be used on non-immutable text nodes",
"15": "spliceText: can only be used on non-immutable text nodes",
"16": "spliceText: selection not found",
"17": "splitText: can only be used on non-immutable text nodes",
"18": "Point.getNode: node not found",
"19": "createNodeFromParse: type \"%s\" + not found",
"20": "select: cannot be called on root nodes",
"21": "remove: cannot be called on root nodes",
"22": "replace: cannot be called on root nodes",
"23": "insertBefore: cannot be called on root nodes",
"24": "insertAfter: cannot be called on root nodes",
"25": "rootNode.append: Only block nodes can be appended to the root node",
"26": "OutlineNode: Node type %s does not implement .clone().",
"27": "Expected node %s to have a parent.",
"28": "Expected node %s to have a parent block.",
"29": "Expected node %s to have a top parent block.",
"30": "getNodesBetween: ancestor is null",
"31": "getLatest: node not found",
"32": "createDOM: base method not extended",
"33": "updateDOM: base method not extended",
"34": "setFlags: can only be used on non-immutable nodes",
"35": "Expected node with key %s to exist but it's not in the nodeMap.",
"36": "Cannot use method in read-only mode.",
"37": "Unable to find an active editor state. State helpers or node methods can only be used synchronously during the callback of editor.update() or editorState.read().",
"38": "Unable to find an active editor. This method can only be used synchronously during the callback of editor.update().",
"39": "updateEditor: the pending editor state is empty. Ensure the root not never becomes empty from an update.",
"40": "updateEditor: selection has been lost because the previously selected nodes have been removed and selection wasn't moved to another node. Ensure selection changes after removing/replacing a selected node.",
"41": "createNode: node does not exist in nodeMap",
"42": "reconcileNode: prevNode or nextNode does not exist in nodeMap",
"43": "reconcileNode: parentDOM is null",
"44": "Reconciliation: could not find DOM element for node key \"${key}\""
}