Skip to content

Commit

Permalink
Avoid marking the whole state as dirty when registering a node transform
Browse files Browse the repository at this point in the history
  • Loading branch information
etrepum committed Dec 13, 2024
1 parent 503894f commit cd6cce1
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 31 deletions.
12 changes: 8 additions & 4 deletions packages/lexical-playground/src/nodes/AutocompleteNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,16 @@ export class AutocompleteNode extends TextNode {
return {element: null};
}

excludeFromCopy() {
return true;
}

createDOM(config: EditorConfig): HTMLElement {
if (this.__uuid !== UUID) {
return document.createElement('span');
}
const dom = super.createDOM(config);
dom.classList.add(config.theme.autocomplete);
if (this.__uuid !== UUID) {
dom.style.display = 'none';
}
return dom;
}
}
Expand All @@ -95,5 +99,5 @@ export function $createAutocompleteNode(
text: string,
uuid: string,
): AutocompleteNode {
return new AutocompleteNode(text, uuid);
return new AutocompleteNode(text, uuid).setMode('token');
}
7 changes: 5 additions & 2 deletions packages/lexical/src/LexicalEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {
getCachedTypeToNodeMap,
getDefaultView,
getDOMSelection,
markAllNodesAsDirty,
markNodesWithTypesAsDirty,
} from './LexicalUtils';
import {ArtificialNode__DO_NOT_USE} from './nodes/ArtificialNode';
import {DecoratorNode} from './nodes/LexicalDecoratorNode';
Expand Down Expand Up @@ -970,7 +970,10 @@ export class LexicalEditor {
registeredNodes.push(registeredReplaceWithNode);
}

markAllNodesAsDirty(this, klass.getType());
markNodesWithTypesAsDirty(
this,
registeredNodes.map((node) => node.klass.getType()),
);
return () => {
registeredNodes.forEach((node) =>
node.transforms.delete(listener as Transform<LexicalNode>),
Expand Down
67 changes: 42 additions & 25 deletions packages/lexical/src/LexicalUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ import {
internalGetActiveEditorState,
isCurrentlyReadOnlyMode,
triggerCommandListeners,
updateEditor,
} from './LexicalUpdates';

export const emptyFunction = () => {
Expand Down Expand Up @@ -498,22 +497,31 @@ export function getEditorStateTextContent(editorState: EditorState): string {
return editorState.read(() => $getRoot().getTextContent());
}

export function markAllNodesAsDirty(editor: LexicalEditor, type: string): void {
// Mark all existing text nodes as dirty
updateEditor(
editor,
export function markNodesWithTypesAsDirty(
editor: LexicalEditor,
types: string[],
): void {
// We only need to mark nodes dirty if they were in the previous state.
// If they aren't, then they are by definition dirty already.
const cachedMap = getCachedTypeToNodeMap(editor.getEditorState());
const dirtyNodeMaps: NodeMap[] = [];
for (const type of types) {
const nodeMap = cachedMap.get(type);
if (nodeMap) {
// By construction these are non-empty
dirtyNodeMaps.push(nodeMap);
}
}
// Nothing to mark dirty, no update necessary
if (dirtyNodeMaps.length === 0) {
return;
}
editor.update(
() => {
const editorState = getActiveEditorState();
if (editorState.isEmpty()) {
return;
}
if (type === 'root') {
$getRoot().markDirty();
return;
}
const nodeMap = editorState._nodeMap;
for (const [, node] of nodeMap) {
node.markDirty();
for (const nodeMap of dirtyNodeMaps) {
for (const node of nodeMap.values()) {
node.markDirty();
}
}
},
editor._pendingEditorState === null
Expand Down Expand Up @@ -1825,17 +1833,26 @@ export function getCachedTypeToNodeMap(
);
let typeToNodeMap = cachedNodeMaps.get(editorState);
if (!typeToNodeMap) {
typeToNodeMap = new Map();
typeToNodeMap = computeTypeToNodeMap(editorState);
cachedNodeMaps.set(editorState, typeToNodeMap);
for (const [nodeKey, node] of editorState._nodeMap) {
const nodeType = node.__type;
let nodeMap = typeToNodeMap.get(nodeType);
if (!nodeMap) {
nodeMap = new Map();
typeToNodeMap.set(nodeType, nodeMap);
}
nodeMap.set(nodeKey, node);
}
return typeToNodeMap;
}

/**
* @internal
* Compute a Map of node type to nodes for an EditorState
*/
function computeTypeToNodeMap(editorState: EditorState): TypeToNodeMap {
const typeToNodeMap = new Map();
for (const [nodeKey, node] of editorState._nodeMap) {
const nodeType = node.__type;
let nodeMap = typeToNodeMap.get(nodeType);
if (!nodeMap) {
nodeMap = new Map();
typeToNodeMap.set(nodeType, nodeMap);
}
nodeMap.set(nodeKey, node);
}
return typeToNodeMap;
}
Expand Down
1 change: 1 addition & 0 deletions packages/lexical/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export type {
SerializedEditor,
Spread,
Transform,
UpdateListener,
} from './LexicalEditor';
export type {
EditorState,
Expand Down

0 comments on commit cd6cce1

Please sign in to comment.