2 * Copyright (c) Meta Platforms, Inc. and affiliates.
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
9 import type {ElementNode} from '.';
10 import type {LexicalEditor} from './LexicalEditor';
11 import type {EditorState} from './LexicalEditorState';
12 import type {NodeKey, NodeMap} from './LexicalNode';
14 import {$isElementNode} from '.';
15 import {cloneDecorators} from './LexicalUtils';
17 export function $garbageCollectDetachedDecorators(
18 editor: LexicalEditor,
19 pendingEditorState: EditorState,
21 const currentDecorators = editor._decorators;
22 const pendingDecorators = editor._pendingDecorators;
23 let decorators = pendingDecorators || currentDecorators;
24 const nodeMap = pendingEditorState._nodeMap;
27 for (key in decorators) {
28 if (!nodeMap.has(key)) {
29 if (decorators === currentDecorators) {
30 decorators = cloneDecorators(editor);
33 delete decorators[key];
38 type IntentionallyMarkedAsDirtyElement = boolean;
40 function $garbageCollectDetachedDeepChildNodes(
45 nodeMapDelete: Array<NodeKey>,
46 dirtyNodes: Map<NodeKey, IntentionallyMarkedAsDirtyElement>,
48 let child = node.getFirstChild();
50 while (child !== null) {
51 const childKey = child.__key;
52 // TODO Revise condition below, redundant? LexicalNode already cleans up children when moving Nodes
53 if (child.__parent === parentKey) {
54 if ($isElementNode(child)) {
55 $garbageCollectDetachedDeepChildNodes(
65 // If we have created a node and it was dereferenced, then also
66 // remove it from out dirty nodes Set.
67 if (!prevNodeMap.has(childKey)) {
68 dirtyNodes.delete(childKey);
70 nodeMapDelete.push(childKey);
72 child = child.getNextSibling();
76 export function $garbageCollectDetachedNodes(
77 prevEditorState: EditorState,
78 editorState: EditorState,
79 dirtyLeaves: Set<NodeKey>,
80 dirtyElements: Map<NodeKey, IntentionallyMarkedAsDirtyElement>,
82 const prevNodeMap = prevEditorState._nodeMap;
83 const nodeMap = editorState._nodeMap;
84 // Store dirtyElements in a queue for later deletion; deleting dirty subtrees too early will
85 // hinder accessing .__next on child nodes
86 const nodeMapDelete: Array<NodeKey> = [];
88 for (const [nodeKey] of dirtyElements) {
89 const node = nodeMap.get(nodeKey);
90 if (node !== undefined) {
91 // Garbage collect node and its children if they exist
92 if (!node.isAttached()) {
93 if ($isElementNode(node)) {
94 $garbageCollectDetachedDeepChildNodes(
103 // If we have created a node and it was dereferenced, then also
104 // remove it from out dirty nodes Set.
105 if (!prevNodeMap.has(nodeKey)) {
106 dirtyElements.delete(nodeKey);
108 nodeMapDelete.push(nodeKey);
112 for (const nodeKey of nodeMapDelete) {
113 nodeMap.delete(nodeKey);
116 for (const nodeKey of dirtyLeaves) {
117 const node = nodeMap.get(nodeKey);
118 if (node !== undefined && !node.isAttached()) {
119 if (!prevNodeMap.has(nodeKey)) {
120 dirtyLeaves.delete(nodeKey);
122 nodeMap.delete(nodeKey);