126 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
		
		
			
		
	
	
			126 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Copyright (c) Meta Platforms, Inc. and affiliates.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * This source code is licensed under the MIT license found in the
							 | 
						||
| 
								 | 
							
								 * LICENSE file in the root directory of this source tree.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import type {ElementNode} from '.';
							 | 
						||
| 
								 | 
							
								import type {LexicalEditor} from './LexicalEditor';
							 | 
						||
| 
								 | 
							
								import type {EditorState} from './LexicalEditorState';
							 | 
						||
| 
								 | 
							
								import type {NodeKey, NodeMap} from './LexicalNode';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import {$isElementNode} from '.';
							 | 
						||
| 
								 | 
							
								import {cloneDecorators} from './LexicalUtils';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export function $garbageCollectDetachedDecorators(
							 | 
						||
| 
								 | 
							
								  editor: LexicalEditor,
							 | 
						||
| 
								 | 
							
								  pendingEditorState: EditorState,
							 | 
						||
| 
								 | 
							
								): void {
							 | 
						||
| 
								 | 
							
								  const currentDecorators = editor._decorators;
							 | 
						||
| 
								 | 
							
								  const pendingDecorators = editor._pendingDecorators;
							 | 
						||
| 
								 | 
							
								  let decorators = pendingDecorators || currentDecorators;
							 | 
						||
| 
								 | 
							
								  const nodeMap = pendingEditorState._nodeMap;
							 | 
						||
| 
								 | 
							
								  let key;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (key in decorators) {
							 | 
						||
| 
								 | 
							
								    if (!nodeMap.has(key)) {
							 | 
						||
| 
								 | 
							
								      if (decorators === currentDecorators) {
							 | 
						||
| 
								 | 
							
								        decorators = cloneDecorators(editor);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      delete decorators[key];
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type IntentionallyMarkedAsDirtyElement = boolean;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function $garbageCollectDetachedDeepChildNodes(
							 | 
						||
| 
								 | 
							
								  node: ElementNode,
							 | 
						||
| 
								 | 
							
								  parentKey: NodeKey,
							 | 
						||
| 
								 | 
							
								  prevNodeMap: NodeMap,
							 | 
						||
| 
								 | 
							
								  nodeMap: NodeMap,
							 | 
						||
| 
								 | 
							
								  nodeMapDelete: Array<NodeKey>,
							 | 
						||
| 
								 | 
							
								  dirtyNodes: Map<NodeKey, IntentionallyMarkedAsDirtyElement>,
							 | 
						||
| 
								 | 
							
								): void {
							 | 
						||
| 
								 | 
							
								  let child = node.getFirstChild();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  while (child !== null) {
							 | 
						||
| 
								 | 
							
								    const childKey = child.__key;
							 | 
						||
| 
								 | 
							
								    // TODO Revise condition below, redundant? LexicalNode already cleans up children when moving Nodes
							 | 
						||
| 
								 | 
							
								    if (child.__parent === parentKey) {
							 | 
						||
| 
								 | 
							
								      if ($isElementNode(child)) {
							 | 
						||
| 
								 | 
							
								        $garbageCollectDetachedDeepChildNodes(
							 | 
						||
| 
								 | 
							
								          child,
							 | 
						||
| 
								 | 
							
								          childKey,
							 | 
						||
| 
								 | 
							
								          prevNodeMap,
							 | 
						||
| 
								 | 
							
								          nodeMap,
							 | 
						||
| 
								 | 
							
								          nodeMapDelete,
							 | 
						||
| 
								 | 
							
								          dirtyNodes,
							 | 
						||
| 
								 | 
							
								        );
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // If we have created a node and it was dereferenced, then also
							 | 
						||
| 
								 | 
							
								      // remove it from out dirty nodes Set.
							 | 
						||
| 
								 | 
							
								      if (!prevNodeMap.has(childKey)) {
							 | 
						||
| 
								 | 
							
								        dirtyNodes.delete(childKey);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      nodeMapDelete.push(childKey);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    child = child.getNextSibling();
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export function $garbageCollectDetachedNodes(
							 | 
						||
| 
								 | 
							
								  prevEditorState: EditorState,
							 | 
						||
| 
								 | 
							
								  editorState: EditorState,
							 | 
						||
| 
								 | 
							
								  dirtyLeaves: Set<NodeKey>,
							 | 
						||
| 
								 | 
							
								  dirtyElements: Map<NodeKey, IntentionallyMarkedAsDirtyElement>,
							 | 
						||
| 
								 | 
							
								): void {
							 | 
						||
| 
								 | 
							
								  const prevNodeMap = prevEditorState._nodeMap;
							 | 
						||
| 
								 | 
							
								  const nodeMap = editorState._nodeMap;
							 | 
						||
| 
								 | 
							
								  // Store dirtyElements in a queue for later deletion; deleting dirty subtrees too early will
							 | 
						||
| 
								 | 
							
								  // hinder accessing .__next on child nodes
							 | 
						||
| 
								 | 
							
								  const nodeMapDelete: Array<NodeKey> = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (const [nodeKey] of dirtyElements) {
							 | 
						||
| 
								 | 
							
								    const node = nodeMap.get(nodeKey);
							 | 
						||
| 
								 | 
							
								    if (node !== undefined) {
							 | 
						||
| 
								 | 
							
								      // Garbage collect node and its children if they exist
							 | 
						||
| 
								 | 
							
								      if (!node.isAttached()) {
							 | 
						||
| 
								 | 
							
								        if ($isElementNode(node)) {
							 | 
						||
| 
								 | 
							
								          $garbageCollectDetachedDeepChildNodes(
							 | 
						||
| 
								 | 
							
								            node,
							 | 
						||
| 
								 | 
							
								            nodeKey,
							 | 
						||
| 
								 | 
							
								            prevNodeMap,
							 | 
						||
| 
								 | 
							
								            nodeMap,
							 | 
						||
| 
								 | 
							
								            nodeMapDelete,
							 | 
						||
| 
								 | 
							
								            dirtyElements,
							 | 
						||
| 
								 | 
							
								          );
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        // If we have created a node and it was dereferenced, then also
							 | 
						||
| 
								 | 
							
								        // remove it from out dirty nodes Set.
							 | 
						||
| 
								 | 
							
								        if (!prevNodeMap.has(nodeKey)) {
							 | 
						||
| 
								 | 
							
								          dirtyElements.delete(nodeKey);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        nodeMapDelete.push(nodeKey);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  for (const nodeKey of nodeMapDelete) {
							 | 
						||
| 
								 | 
							
								    nodeMap.delete(nodeKey);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (const nodeKey of dirtyLeaves) {
							 | 
						||
| 
								 | 
							
								    const node = nodeMap.get(nodeKey);
							 | 
						||
| 
								 | 
							
								    if (node !== undefined && !node.isAttached()) {
							 | 
						||
| 
								 | 
							
								      if (!prevNodeMap.has(nodeKey)) {
							 | 
						||
| 
								 | 
							
								        dirtyLeaves.delete(nodeKey);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      nodeMap.delete(nodeKey);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 |