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

Commit a01a1d9

Browse files
committed
memory editor integration (+ layout/renderer)
1 parent 0c653e9 commit a01a1d9

File tree

8 files changed

+891
-408
lines changed

8 files changed

+891
-408
lines changed

packages/core/src/Dispatcher.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ export interface CommandImplementation extends Contextual {
1010
export interface CommandParams {
1111
context?: Context;
1212
}
13+
export interface CommitParams extends CommandParams {
14+
add: object[];
15+
move: object[];
16+
remove: object[];
17+
update: [object, string[] | number[] | void][];
18+
}
1319
export type CommandHandler = (args) => void;
1420
export type CommandHook = (params: CommandParams, commandId: string) => void;
1521

packages/core/src/JWEditor.ts

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Dispatcher } from './Dispatcher';
1+
import { Dispatcher, CommitParams } from './Dispatcher';
22
import { JWPlugin, JWPluginConfig } from './JWPlugin';
33
import { Core } from './Core';
44
import { ContextManager } from './ContextManager';
@@ -10,6 +10,9 @@ import { ContainerNode } from './VNodes/ContainerNode';
1010
import { AtomicNode } from './VNodes/AtomicNode';
1111
import { SeparatorNode } from './VNodes/SeparatorNode';
1212
import { ModeIdentifier, ModeDefinition, Mode } from './Mode';
13+
import { Memory } from './Memory/Memory';
14+
import { makeVersionable } from './Memory/Versionable';
15+
import { VersionableArray } from './Memory/VersionableArray';
1316

1417
export enum EditorStage {
1518
CONFIGURATION = 'configuration',
@@ -68,7 +71,10 @@ export class JWEditor {
6871
plugins: [],
6972
loadables: {},
7073
};
71-
selection = new VSelection(this);
74+
memory: Memory;
75+
memoryID = 0;
76+
memoryInfo: { commandNames: string[] };
77+
selection: VSelection;
7278
loaders: Record<string, Loader> = {};
7379
private mutex = Promise.resolve();
7480
// Use a set so that when asynchronous functions are called we ensure that
@@ -86,6 +92,7 @@ export class JWEditor {
8692
constructor() {
8793
this.dispatcher = new Dispatcher(this);
8894
this.plugins = new Map();
95+
this.selection = new VSelection(this);
8996
this.contextManager = new ContextManager(this);
9097

9198
this.nextEventMutex = this.nextEventMutex.bind(this);
@@ -130,9 +137,18 @@ export class JWEditor {
130137
this.setMode(this.configuration.mode);
131138
}
132139

133-
for (const plugin of this.plugins.values()) {
134-
await plugin.start();
135-
}
140+
// create memory
141+
this.memoryInfo = makeVersionable({ commandNames: [] });
142+
this.memory = new Memory();
143+
this.memory.linkToMemory(this.memoryInfo);
144+
this.memory.create(this.memoryID.toString());
145+
146+
// Start all plugins in the first memory slice.
147+
return this.execBatch(async () => {
148+
for (const plugin of this.plugins.values()) {
149+
await plugin.start();
150+
}
151+
});
136152
}
137153

138154
//--------------------------------------------------------------------------
@@ -303,11 +319,16 @@ export class JWEditor {
303319
}
304320
}
305321

306-
async execBatch(callback: () => Promise<void>): Promise<void> {
307-
this.preventRenders.add(callback);
308-
await callback();
309-
this.preventRenders.delete(callback);
310-
await this.dispatcher.dispatchHooks('@batch');
322+
/**
323+
* Execute arbitrary code in `callback`, then dispatch the commit event.
324+
*
325+
* @param callback
326+
*/
327+
async execBatch(callback: () => Promise<void> | void): Promise<void> {
328+
return this._execBatchInMemory(() => {
329+
this.memoryInfo.commandNames.push('@batch');
330+
return callback();
331+
});
311332
}
312333

313334
/**
@@ -320,26 +341,61 @@ export class JWEditor {
320341
commandName: C,
321342
params?: CommandParams<P, C>,
322343
): Promise<void> {
323-
return await this.dispatcher.dispatch(commandName, params);
344+
await this._execBatchInMemory(() => {
345+
this.memoryInfo.commandNames.push(commandName);
346+
return this.dispatcher.dispatch(commandName, params);
347+
});
324348
}
325349

326350
/**
327-
* Execute arbitrary code in `callback`, then dispatch the event.
351+
* Execute arbitrary code in `callback` in a free memory slice.
352+
* Return true if we open a memory slice.
353+
*
354+
* TODO: create memory for each plugin who use the command then use
355+
* squashInto(winnerSliceKey, winnerSliceKey, newMasterSliceKey)
356+
*
357+
* @param callback
328358
*/
329-
async execCustomCommand<P extends JWPlugin, C extends Commands<P> = Commands<P>>(
330-
callback: () => Promise<void>,
331-
): Promise<void> {
359+
private async _execBatchInMemory(callback: () => Promise<void> | void): Promise<void> {
360+
const isFrozen = this.memory.isFrozen();
361+
const previousSlice = (this.memoryID - 1).toString();
362+
const memorySlice = this.memoryID.toString();
363+
if (isFrozen) {
364+
// Switch to the next memory slice (unfreeze the memory).
365+
this.memory.switchTo(memorySlice);
366+
this.memoryInfo.commandNames = new VersionableArray();
367+
}
332368
await callback();
333-
await this.dispatcher.dispatchHooks('@custom');
369+
if (isFrozen) {
370+
// Check if it's frozen for calling execCommand inside a call of
371+
// execCommand Create the next memory slice (and freeze the
372+
// current memory).
373+
this.memoryID++;
374+
this.memory.create(this.memoryID.toString());
375+
376+
// Send the commit message with a froozen memory.
377+
const diff: CommitParams = this.memory.getChanges(previousSlice, memorySlice);
378+
await this.dispatcher.dispatchHooks('@commit', diff);
379+
}
334380
}
335381

336382
/**
337383
* Stop this editor instance.
338384
*/
339385
async stop(): Promise<void> {
386+
if (this.memory) {
387+
// Unfreeze the memory.
388+
this.memory.create('stop');
389+
this.memory.switchTo('stop');
390+
this.memory = null;
391+
}
340392
for (const plugin of this.plugins.values()) {
341393
await plugin.stop();
342394
}
395+
this.plugins.clear();
396+
this.dispatcher = new Dispatcher(this);
397+
this.selection = new VSelection(this);
398+
this.contextManager = new ContextManager(this);
343399
// Clear loaders.
344400
this.loaders = {};
345401
this._stage = EditorStage.CONFIGURATION;

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

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,12 @@ import { ZoneXmlDomParser } from './ZoneXmlDomParser';
1515
import { LayoutContainerDomObjectRenderer } from './LayoutContainerDomObjectRenderer';
1616
import { ZoneIdentifier, ZoneNode } from '../../plugin-layout/src/ZoneNode';
1717
import { Keymap } from '../../plugin-keymap/src/Keymap';
18-
import { CommandIdentifier } from '../../core/src/Dispatcher';
18+
import { CommandIdentifier, CommitParams } from '../../core/src/Dispatcher';
1919
import { ActionableDomObjectRenderer } from './ActionableDomObjectRenderer';
2020
import { ActionableGroupDomObjectRenderer } from './ActionableGroupDomObjectRenderer';
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';
2726

@@ -30,7 +29,6 @@ export interface DomLayoutConfig extends JWPluginConfig {
3029
locations?: [ComponentId, DomLayoutLocation][];
3130
components?: ComponentDefinition[];
3231
componentZones?: [ComponentId, ZoneIdentifier[]][];
33-
afterRender?: Function;
3432
}
3533

3634
export class DomLayout<T extends DomLayoutConfig = DomLayoutConfig> extends JWPlugin<T> {
@@ -53,7 +51,7 @@ export class DomLayout<T extends DomLayoutConfig = DomLayoutConfig> extends JWPl
5351
domLocations: this._loadComponentLocations,
5452
};
5553
commandHooks = {
56-
'*': this.redraw,
54+
'@commit': this._redraw,
5755
};
5856

5957
constructor(editor: JWEditor, configuration: T) {
@@ -76,16 +74,14 @@ export class DomLayout<T extends DomLayoutConfig = DomLayoutConfig> extends JWPl
7674
this._loadComponentLocations(this.configuration.locations || []);
7775
domLayoutEngine.location = this.configuration.location;
7876
await domLayoutEngine.start();
79-
if (this.configuration.afterRender) {
80-
await this.configuration.afterRender();
81-
}
8277
window.addEventListener('keydown', this.processKeydown, true);
8378
}
8479
async stop(): Promise<void> {
8580
window.removeEventListener('keydown', this.processKeydown, true);
8681
const layout = this.dependencies.get(Layout);
8782
const domLayoutEngine = layout.engines.dom;
8883
await domLayoutEngine.stop();
84+
return super.stop();
8985
}
9086

9187
//--------------------------------------------------------------------------
@@ -122,25 +118,6 @@ export class DomLayout<T extends DomLayoutConfig = DomLayoutConfig> extends JWPl
122118
}
123119
}
124120

125-
async redraw(): Promise<void> {
126-
// TODO: adapt when add memory
127-
// * redraw node with change
128-
// * redraw children if add or remove children (except for selection)
129-
const layout = this.dependencies.get(Layout);
130-
const domLayoutEngine = layout.engines.dom as DomLayoutEngine;
131-
const editables = domLayoutEngine.components.get('editable');
132-
if (editables?.length) {
133-
const nodes = [...editables];
134-
for (const node of nodes) {
135-
if (node instanceof ContainerNode) {
136-
nodes.push(...node.childVNodes);
137-
}
138-
}
139-
await domLayoutEngine.redraw(...nodes);
140-
await this.configuration.afterRender?.();
141-
}
142-
}
143-
144121
/**
145122
* Return true if the target node is inside Jabberwock's main editable Zone.
146123
*
@@ -202,4 +179,9 @@ export class DomLayout<T extends DomLayoutConfig = DomLayoutConfig> extends JWPl
202179
}
203180
return isAnchorDescendantOfTarget ? anchorNode : target;
204181
}
182+
private async _redraw(params: CommitParams): Promise<void> {
183+
const layout = this.dependencies.get(Layout);
184+
const domLayoutEngine = layout.engines.dom as DomLayoutEngine;
185+
await domLayoutEngine.redraw(params);
186+
}
205187
}

0 commit comments

Comments
 (0)