Skip to content
This repository was archived by the owner on May 5, 2021. It is now read-only.

Commit 976b653

Browse files
committed
perf: efficient use of memory linked to caches
1 parent 4c0d2cd commit 976b653

File tree

6 files changed

+279
-98
lines changed

6 files changed

+279
-98
lines changed

packages/plugin-dom-layout/src/DomLayout.ts

+56-20
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ import { ActionableGroupDomObjectRenderer } from './ActionableGroupDomObjectRend
2121
import { ActionableGroupSelectItemDomObjectRenderer } from './ActionableGroupSelectItemDomObjectRenderer';
2222
import { LabelDomObjectRenderer } from './LabelDomObjectRenderer';
2323
import { SeparatorDomObjectRenderer } from './SeparatorDomObjectRenderer';
24-
import { ContainerNode } from '../../core/src/VNodes/ContainerNode';
2524
import { RuleProperty } from '../../core/src/Mode';
2625
import { isContentEditable } from '../../utils/src/utils';
26+
import { AbstractNode } from '../../core/src/VNodes/AbstractNode';
27+
import { VNode } from '../../core/src/VNodes/VNode';
2728

2829
export interface DomLayoutConfig extends JWPluginConfig {
2930
location?: [Node, DomZonePosition];
@@ -52,7 +53,7 @@ export class DomLayout<T extends DomLayoutConfig = DomLayoutConfig> extends JWPl
5253
domLocations: this._loadComponentLocations,
5354
};
5455
commandHooks = {
55-
'commit': this.redraw,
56+
'commit': this._redraw,
5657
};
5758

5859
constructor(editor: JWEditor, configuration: T) {
@@ -118,24 +119,6 @@ export class DomLayout<T extends DomLayoutConfig = DomLayoutConfig> extends JWPl
118119
}
119120
}
120121

121-
async redraw(): Promise<void> {
122-
// TODO: adapt when add memory
123-
// * redraw node with change
124-
// * redraw children if add or remove children (except for selection)
125-
const layout = this.dependencies.get(Layout);
126-
const domLayoutEngine = layout.engines.dom as DomLayoutEngine;
127-
const editables = domLayoutEngine.components.get('editable');
128-
if (editables?.length) {
129-
const nodes = [...editables];
130-
for (const node of nodes) {
131-
if (node instanceof ContainerNode) {
132-
nodes.push(...node.childVNodes);
133-
}
134-
}
135-
await domLayoutEngine.redraw(nodes);
136-
}
137-
}
138-
139122
/**
140123
* Return true if the target node is inside Jabberwock's main editable Zone.
141124
*
@@ -197,4 +180,57 @@ export class DomLayout<T extends DomLayoutConfig = DomLayoutConfig> extends JWPl
197180
}
198181
return isAnchorDescendantOfTarget ? anchorNode : target;
199182
}
183+
private async _redraw(): Promise<void> {
184+
const layout = this.dependencies.get(Layout);
185+
const domLayoutEngine = layout.engines.dom as DomLayoutEngine;
186+
const changedPath = new Map<AbstractNode, string[][]>();
187+
const pathChanges = this.editor.memory.getChanges();
188+
for (const [object, path] of pathChanges) {
189+
if (object instanceof AbstractNode) {
190+
if (!changedPath.has(object)) {
191+
changedPath.set(object, []);
192+
}
193+
changedPath.get(object).push(path);
194+
}
195+
for (const [parent, path] of this.editor.memory.getParents(object)) {
196+
if (parent instanceof AbstractNode) {
197+
if (!changedPath.has(parent)) {
198+
changedPath.set(parent, []);
199+
}
200+
changedPath.set(parent as VNode, path);
201+
}
202+
}
203+
}
204+
if (changedPath.size) {
205+
const nodes = [];
206+
for (const [root] of changedPath) {
207+
if (root === this.editor.selection.anchor || root === this.editor.selection.focus) {
208+
// Filter not VNode changes and selection change.
209+
changedPath.delete(root);
210+
}
211+
}
212+
let removedNode = false;
213+
for (const [root] of changedPath) {
214+
nodes.push(root);
215+
if (!root.parent) {
216+
removedNode = true;
217+
}
218+
}
219+
if (removedNode) {
220+
// Need to force redrawing of children if remove a child.
221+
for (const [node, paths] of changedPath) {
222+
if (paths.find(path => path[0] === 'childVNodes')) {
223+
for (const child of node.childVNodes) {
224+
const index = nodes.indexOf(child);
225+
if (index !== -1) {
226+
nodes.splice(index, 1);
227+
}
228+
nodes.push(child);
229+
}
230+
}
231+
}
232+
}
233+
await domLayoutEngine.redraw(nodes);
234+
}
235+
}
200236
}

packages/plugin-dom-layout/src/DomLayoutEngine.ts

+49-24
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
} from '../../plugin-renderer-dom-object/src/DomObjectRenderingEngine';
2020
import { VElement } from '../../core/src/VNodes/VElement';
2121
import { flat } from '../../utils/src/utils';
22+
import { AbstractNode } from '../../core/src/VNodes/AbstractNode';
2223

2324
export type DomPoint = [Node, number];
2425
export type DomLayoutLocation = [Node, DomZonePosition];
@@ -116,44 +117,71 @@ export class DomLayoutEngine extends LayoutEngine {
116117
return this._domReconciliationEngine.toDom(node);
117118
}
118119
async redraw(nodes?: VNode[]): Promise<void> {
119-
if (
120-
!this.editor.enableRender ||
121-
(this.editor.preventRenders && this.editor.preventRenders.size)
122-
) {
123-
return;
124-
}
125120
if (this._currentlyRedrawing) {
126121
throw new Error('Double redraw detected');
127122
}
128123
this._currentlyRedrawing = true;
129124

130125
if (nodes) {
126+
nodes = nodes.filter(node => {
127+
return node.ancestors().pop() === this.root;
128+
});
129+
130+
const nodeIds: Record<number, VNode> = {};
131131
for (let node of nodes) {
132+
nodeIds[node.id] = node;
132133
while (
134+
node.parent &&
135+
!nodeIds[node.parent.id] &&
133136
(this._domReconciliationEngine.getRenderedWith(node).length !== 1 ||
134-
!this._domReconciliationEngine.toDom(node).length) &&
135-
node.parent
137+
!this._domReconciliationEngine.toDom(node).length)
136138
) {
137139
// If the node are redererd with some other nodes then redraw parent.
138140
// If not in layout then redraw the parent.
139141
node = node.parent;
140-
if (!nodes.includes(node)) {
141-
nodes.push(node);
142-
}
142+
nodeIds[node.id] = node;
143143
}
144144
}
145-
for (const node of [...nodes]) {
145+
146+
const parents: VNode[] = [];
147+
for (const node of nodes) {
146148
// Add direct siblings nodes for batched nodes with format.
149+
const next = node.next();
150+
if (next && next.parent === node.parent) {
151+
nodeIds[next.id] = next;
152+
}
147153
const previous = node.previous();
148-
if (previous && !nodes.includes(previous)) {
149-
nodes.push(previous);
154+
if (previous && previous.parent === node.parent) {
155+
nodeIds[previous.id] = previous;
150156
}
151-
const next = node.next();
152-
if (next && !nodes.includes(next)) {
153-
nodes.push(next);
157+
}
158+
159+
// Add grouped node for rendering.
160+
const nearests: Record<number, VNode> = {};
161+
for (const id in nodeIds) {
162+
if (!nearests[id]) {
163+
const node = nodeIds[id];
164+
for (const nearest of this._domReconciliationEngine.getRenderedWith(node)) {
165+
if (nearest instanceof AbstractNode) {
166+
const id = nearest.id;
167+
nodeIds[id] = nearests[id] = nearest;
168+
}
169+
}
170+
// Add parent for wrap/unwrapped node list formatted charNodes.
171+
const parent = node.parent;
172+
if (parent && !parents.includes(parent)) {
173+
parents.push(parent);
174+
}
154175
}
155176
}
177+
178+
for (const parent of parents) {
179+
nodeIds[parent.id] = parent;
180+
}
181+
182+
nodes = Object.values(nodeIds);
156183
} else {
184+
nodes = [];
157185
// Redraw all.
158186
for (const componentId in this.locations) {
159187
nodes.push(...this.components.get(componentId));
@@ -165,18 +193,15 @@ export class DomLayoutEngine extends LayoutEngine {
165193
}
166194
}
167195

168-
nodes = nodes.filter(node => {
169-
const ancestor = node.ancestors(ZoneNode).pop();
170-
return ancestor?.managedZones.includes('root');
171-
});
172-
173196
// Render nodes.
174197
const renderer = this.editor.plugins.get(Renderer);
175-
const domObjects = await renderer.render<DomObject>('dom/object', nodes);
198+
let domObjects = (await renderer.render<DomObject>('dom/object', nodes)) || [];
176199
const engine = renderer.engines['dom/object'] as DomObjectRenderingEngine;
177200

201+
domObjects = domObjects.filter((node, index) => domObjects.indexOf(node) === index);
202+
178203
this._domReconciliationEngine.update(
179-
domObjects || [],
204+
domObjects,
180205
engine.locations,
181206
engine.from,
182207
this._markedForRedraw,

packages/plugin-dom-layout/src/DomReconciliationEngine.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ export class DomReconciliationEngine {
225225
if (id) {
226226
if (this._diff[id]) {
227227
this._diff[id].askCompleteRedrawing = true;
228-
} else {
228+
} else if (this._objects[id]) {
229229
const domObject = this._objects[id].object;
230230
this._diff[id] = {
231231
id: id,
@@ -328,6 +328,7 @@ export class DomReconciliationEngine {
328328
object.object.attach(...object.dom);
329329
}
330330
}
331+
this._domUpdated.clear();
331332
}
332333

333334
/**

0 commit comments

Comments
 (0)