]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/lexical/core/LexicalGC.ts
Opensearch: Fixed XML declaration when php short tags enabled
[bookstack] / resources / js / wysiwyg / lexical / core / LexicalGC.ts
1 /**
2  * Copyright (c) Meta Platforms, Inc. and affiliates.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  *
7  */
8
9 import type {ElementNode} from '.';
10 import type {LexicalEditor} from './LexicalEditor';
11 import type {EditorState} from './LexicalEditorState';
12 import type {NodeKey, NodeMap} from './LexicalNode';
13
14 import {$isElementNode} from '.';
15 import {cloneDecorators} from './LexicalUtils';
16
17 export function $garbageCollectDetachedDecorators(
18   editor: LexicalEditor,
19   pendingEditorState: EditorState,
20 ): void {
21   const currentDecorators = editor._decorators;
22   const pendingDecorators = editor._pendingDecorators;
23   let decorators = pendingDecorators || currentDecorators;
24   const nodeMap = pendingEditorState._nodeMap;
25   let key;
26
27   for (key in decorators) {
28     if (!nodeMap.has(key)) {
29       if (decorators === currentDecorators) {
30         decorators = cloneDecorators(editor);
31       }
32
33       delete decorators[key];
34     }
35   }
36 }
37
38 type IntentionallyMarkedAsDirtyElement = boolean;
39
40 function $garbageCollectDetachedDeepChildNodes(
41   node: ElementNode,
42   parentKey: NodeKey,
43   prevNodeMap: NodeMap,
44   nodeMap: NodeMap,
45   nodeMapDelete: Array<NodeKey>,
46   dirtyNodes: Map<NodeKey, IntentionallyMarkedAsDirtyElement>,
47 ): void {
48   let child = node.getFirstChild();
49
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(
56           child,
57           childKey,
58           prevNodeMap,
59           nodeMap,
60           nodeMapDelete,
61           dirtyNodes,
62         );
63       }
64
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);
69       }
70       nodeMapDelete.push(childKey);
71     }
72     child = child.getNextSibling();
73   }
74 }
75
76 export function $garbageCollectDetachedNodes(
77   prevEditorState: EditorState,
78   editorState: EditorState,
79   dirtyLeaves: Set<NodeKey>,
80   dirtyElements: Map<NodeKey, IntentionallyMarkedAsDirtyElement>,
81 ): void {
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> = [];
87
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(
95             node,
96             nodeKey,
97             prevNodeMap,
98             nodeMap,
99             nodeMapDelete,
100             dirtyElements,
101           );
102         }
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);
107         }
108         nodeMapDelete.push(nodeKey);
109       }
110     }
111   }
112   for (const nodeKey of nodeMapDelete) {
113     nodeMap.delete(nodeKey);
114   }
115
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);
121       }
122       nodeMap.delete(nodeKey);
123     }
124   }
125 }