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

Commit c544c12

Browse files
committed
[IMP] Memory,*: plugin integration for memory usage and new rendering
1 parent e59239f commit c544c12

File tree

90 files changed

+3390
-2118
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+3390
-2118
lines changed

examples/layout/layout.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,20 +98,20 @@ editor.configure(Theme, {
9898
.get(Parser)
9999
.parse(
100100
'text/html',
101-
`<div style="background: lightblue; height: 100%;"><t-placeholder/></div>`,
101+
`<div style="background: lightblue; min-height: 100%;"><t-placeholder/></div>`,
102102
);
103103
},
104104
},
105105
{
106106
id: 'iframe',
107107
label: 'Theme color red in iframe',
108108
render: async (editor: JWEditor): Promise<VNode[]> => {
109-
return editor.plugins
110-
.get(Parser)
111-
.parse(
112-
'text/html',
113-
`<t-iframe style="border: 0; width: 100%;"><div style="background: #ffaaaa; height: 100%;"><t-placeholder/></div></t-iframe>`,
114-
);
109+
return editor.plugins.get(Parser).parse(
110+
'text/html',
111+
`<t-iframe style="border-width: 80px 14px 40px; border-radius: 16px; border-style: solid; border-color: #000; background-color: white; width: 400px; height: 600px; margin: auto; display: block;">
112+
<div style="background: #ffaaaa; min-height: 100%;"><t-placeholder/></div>
113+
</t-iframe>`,
114+
);
115115
},
116116
},
117117
],

packages/bundle-odoo-website-editor/OdooWebsiteEditor.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,10 @@ export class OdooWebsiteEditor extends JWEditor {
9696
constructor(options: OdooWebsiteEditorOptions) {
9797
super();
9898
class CustomPlugin extends JWPlugin {
99-
commands = options.customCommands;
99+
commands = Object.assign(
100+
{ commit: { handler: options.afterRender } },
101+
options.customCommands,
102+
);
100103
}
101104

102105
this.configure({
@@ -212,7 +215,6 @@ export class OdooWebsiteEditor extends JWEditor {
212215
['editable', ['main']],
213216
],
214217
location: options.location,
215-
afterRender: options.afterRender,
216218
});
217219
this.configure(DomEditable, {
218220
autoFocus: true,
@@ -237,9 +239,4 @@ export class OdooWebsiteEditor extends JWEditor {
237239
const nodes = await renderer.render<Node[]>('dom/html', editable);
238240
return nodes && nodes[0];
239241
}
240-
241-
async render(): Promise<void> {
242-
const domLayout = this.plugins.get(DomLayout);
243-
return domLayout.redraw();
244-
}
245242
}

packages/bundle-odoo-website-editor/odoo-integration.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { BasicEditor } from '../bundle-basic-editor/BasicEditor';
22
import { DevTools } from '../plugin-devtools/src/DevTools';
33
import { OdooWebsiteEditor } from './OdooWebsiteEditor';
4-
import { VRange, withRange } from '../core/src/VRange';
4+
import { VRange } from '../core/src/VRange';
55
import { DomLayoutEngine } from '../plugin-dom-layout/src/DomLayoutEngine';
66
import { Layout } from '../plugin-layout/src/Layout';
77
import { Renderer } from '../plugin-renderer/src/Renderer';
@@ -27,7 +27,6 @@ export {
2727
DomLayoutEngine,
2828
Renderer,
2929
ImageNode,
30-
withRange,
3130
VRange,
3231
InlineNode,
3332
LinkFormat,

packages/core/src/Core.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export class Core<T extends JWPluginConfig = JWPluginConfig> extends JWPlugin<T>
8585
range.mode.is(range.startContainer, RuleProperty.EDITABLE)
8686
) {
8787
// Otherwise set range start at previous valid leaf.
88-
let ancestor = range.start.parent;
88+
let ancestor: VNode = range.start.parent;
8989
while (
9090
ancestor &&
9191
range.mode.is(ancestor, RuleProperty.BREAKABLE) &&
@@ -129,7 +129,7 @@ export class Core<T extends JWPluginConfig = JWPluginConfig> extends JWPlugin<T>
129129
range.mode.is(range.endContainer, RuleProperty.EDITABLE)
130130
) {
131131
// Otherwise set range end at next valid leaf.
132-
let ancestor = range.end.parent;
132+
let ancestor: VNode = range.end.parent;
133133
while (
134134
ancestor &&
135135
range.mode.is(ancestor, RuleProperty.BREAKABLE) &&

packages/core/src/JWEditor.ts

Lines changed: 114 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
1-
import { Dispatcher } from './Dispatcher';
1+
import { Dispatcher, CommandParams } from './Dispatcher';
22
import { JWPlugin, JWPluginConfig } from './JWPlugin';
33
import { Core } from './Core';
44
import { ContextManager } from './ContextManager';
55
import { VSelection } from './VSelection';
6+
import { VRange } from './VRange';
67
import { isConstructor } from '../../utils/src/utils';
78
import { Keymap } from '../../plugin-keymap/src/Keymap';
89
import { StageError } from '../../utils/src/errors';
910
import { ContainerNode } from './VNodes/ContainerNode';
1011
import { AtomicNode } from './VNodes/AtomicNode';
1112
import { SeparatorNode } from './VNodes/SeparatorNode';
1213
import { ModeIdentifier, ModeDefinition, Mode } from './Mode';
14+
import { Memory, ChangesLocations } from './Memory/Memory';
15+
import { makeVersionable } from './Memory/Versionable';
16+
import { VersionableArray } from './Memory/VersionableArray';
17+
import { Point } from './VNodes/VNode';
1318

1419
export enum EditorStage {
1520
CONFIGURATION = 'configuration',
@@ -26,9 +31,13 @@ export type Loadables<T extends JWPlugin = JWPlugin> = {
2631
};
2732

2833
type Commands<T extends JWPlugin> = Extract<keyof T['commands'], string>;
29-
type CommandParams<T extends JWPlugin, K extends string> = K extends Commands<T>
34+
type CommandParamsType<T extends JWPlugin, K extends string> = K extends Commands<T>
3035
? Parameters<T['commands'][K]['handler']>[0]
3136
: never;
37+
export interface CommitParams extends CommandParams {
38+
changesLocations: ChangesLocations;
39+
commandNames: string[];
40+
}
3241

3342
export interface JWEditorConfig {
3443
/**
@@ -68,7 +77,10 @@ export class JWEditor {
6877
plugins: [],
6978
loadables: {},
7079
};
71-
selection = new VSelection(this);
80+
memory: Memory;
81+
memoryInfo: { commandNames: string[] };
82+
private _memoryID = 0;
83+
selection: VSelection;
7284
loaders: Record<string, Loader> = {};
7385
private mutex = Promise.resolve();
7486
// Use a set so that when asynchronous functions are called we ensure that
@@ -86,6 +98,7 @@ export class JWEditor {
8698
constructor() {
8799
this.dispatcher = new Dispatcher(this);
88100
this.plugins = new Map();
101+
this.selection = new VSelection(this);
89102
this.contextManager = new ContextManager(this);
90103

91104
this.nextEventMutex = this.nextEventMutex.bind(this);
@@ -130,9 +143,18 @@ export class JWEditor {
130143
this.setMode(this.configuration.mode);
131144
}
132145

133-
for (const plugin of this.plugins.values()) {
134-
await plugin.start();
135-
}
146+
// create memory
147+
this.memoryInfo = makeVersionable({ commandNames: [] });
148+
this.memory = new Memory();
149+
this.memory.attach(this.memoryInfo);
150+
this.memory.create(this._memoryID.toString());
151+
152+
// Start all plugins in the first memory slice.
153+
return this.execCommand(async () => {
154+
for (const plugin of this.plugins.values()) {
155+
await plugin.start();
156+
}
157+
});
136158
}
137159

138160
//--------------------------------------------------------------------------
@@ -303,13 +325,12 @@ export class JWEditor {
303325
}
304326
}
305327

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');
311-
}
312-
328+
/**
329+
* Execute arbitrary code in `callback`, then dispatch the commit event.
330+
*
331+
* @param callback
332+
*/
333+
async execCommand(callback: () => Promise<void> | void): Promise<void>;
313334
/**
314335
* Execute the given command.
315336
*
@@ -318,28 +339,101 @@ export class JWEditor {
318339
*/
319340
async execCommand<P extends JWPlugin, C extends Commands<P> = Commands<P>>(
320341
commandName: C,
321-
params?: CommandParams<P, C>,
342+
params?: CommandParamsType<P, C>,
343+
): Promise<void>;
344+
/**
345+
* Execute the command or arbitrary code in `callback` in memory.
346+
*
347+
* TODO: create memory for each plugin who use the command then use
348+
* squashInto(winnerSliceKey, winnerSliceKey, newMasterSliceKey)
349+
*
350+
* @param commandName name identifier of the command to execute or callback
351+
* @param params arguments object of the command to execute
352+
*/
353+
async execCommand<P extends JWPlugin, C extends Commands<P> = Commands<P>>(
354+
commandName: C | (() => Promise<void> | void),
355+
params?: CommandParamsType<P, C>,
322356
): Promise<void> {
323-
return await this.dispatcher.dispatch(commandName, params);
357+
const isFrozen = this.memory.isFrozen();
358+
359+
let memorySlice: string;
360+
if (isFrozen) {
361+
// Switch to the next memory slice (unfreeze the memory).
362+
memorySlice = this._memoryID.toString();
363+
this.memory.switchTo(memorySlice);
364+
this.memoryInfo.commandNames = new VersionableArray();
365+
}
366+
367+
// Execute command.
368+
if (typeof commandName === 'function') {
369+
this.memoryInfo.commandNames.push('@custom');
370+
await commandName();
371+
} else {
372+
this.memoryInfo.commandNames.push(commandName);
373+
await this.dispatcher.dispatch(commandName, params);
374+
}
375+
376+
if (isFrozen) {
377+
// Check if it's frozen for calling execCommand inside a call of
378+
// execCommand Create the next memory slice (and freeze the
379+
// current memory).
380+
this._memoryID++;
381+
const nextMemorySlice = this._memoryID.toString();
382+
this.memory.create(nextMemorySlice);
383+
384+
// Send the commit message with a froozen memory.
385+
const changesLocations = this.memory.getChangesLocations(
386+
memorySlice,
387+
this.memory.sliceKey,
388+
);
389+
await this.dispatcher.dispatchHooks('@commit', {
390+
changesLocations: changesLocations,
391+
commandNames: this.memoryInfo.commandNames,
392+
});
393+
}
324394
}
325395

326396
/**
327-
* Execute arbitrary code in `callback`, then dispatch the event.
397+
* Create a temporary range corresponding to the given boundary points and
398+
* call the given callback with the newly created range as argument. The
399+
* range is automatically destroyed after calling the callback.
400+
*
401+
* @param bounds The points corresponding to the range boundaries.
402+
* @param callback The callback to call with the newly created range.
403+
* @param mode
328404
*/
329-
async execCustomCommand<P extends JWPlugin, C extends Commands<P> = Commands<P>>(
330-
callback: () => Promise<void>,
405+
async withRange(
406+
bounds: [Point, Point],
407+
callback: (range: VRange) => Promise<void> | void,
408+
mode?: Mode,
331409
): Promise<void> {
332-
await callback();
333-
await this.dispatcher.dispatchHooks('@custom');
410+
return this.execCommand(async () => {
411+
this.memoryInfo.commandNames.push('@withRange');
412+
const range = new VRange(this, bounds, mode);
413+
await callback(range);
414+
range.remove();
415+
});
334416
}
335417

336418
/**
337419
* Stop this editor instance.
338420
*/
339421
async stop(): Promise<void> {
422+
if (this.memory) {
423+
this.memory.create('stop');
424+
this.memory.switchTo('stop'); // Unfreeze the memory.
425+
}
340426
for (const plugin of this.plugins.values()) {
341427
await plugin.stop();
342428
}
429+
if (this.memory) {
430+
this.memory.create('stopped'); // Freeze the memory.
431+
this.memory = null;
432+
}
433+
this.plugins.clear();
434+
this.dispatcher = new Dispatcher(this);
435+
this.selection = new VSelection(this);
436+
this.contextManager = new ContextManager(this);
343437
// Clear loaders.
344438
this.loaders = {};
345439
this._stage = EditorStage.CONFIGURATION;

packages/core/src/JWPlugin.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ export class JWPlugin<T extends JWPluginConfig = {}> {
3333
async stop(): Promise<void> {
3434
// This is where plugins can do asynchronous work when the editor is
3535
// stopping (e.g. save on a server, close connections, etc).
36+
this.dependencies.clear();
37+
this.editor = null;
3638
}
3739
}
3840
export interface JWPlugin {

packages/core/src/Memory/test/memory.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { VersionableSet } from '../VersionableSet';
77
import { makeVersionable, markNotVersionable, proxifyVersionable } from '../Versionable';
88
import { memoryProxyPramsKey } from '../const';
99

10-
describe.only('core', () => {
10+
describe('core', () => {
1111
describe('state', () => {
1212
describe('memory', () => {
1313
describe('throw', () => {

packages/core/src/Modifier.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11
import { Constructor } from '../../utils/src/utils';
22
import { VNode } from './VNodes/VNode';
3+
import { VersionableObject } from './Memory/VersionableObject';
34

4-
export type ModifierTypeguard<T extends Modifier> = (
5-
modifier: Modifier,
6-
batch: VNode[],
7-
) => modifier is T;
5+
export type ModifierTypeguard<T extends Modifier> = (modifier: Modifier) => modifier is T;
86
export type ModifierPredicate<T = Modifier | boolean> = T extends Modifier
97
? Constructor<T> | ModifierTypeguard<T>
10-
: (modifier: Modifier, batch: VNode[]) => boolean;
8+
: (modifier: Modifier) => boolean;
119

1210
interface ModifierConstructor {
1311
new <T extends Constructor<Modifier>>(...args: ConstructorParameters<T>): this;
1412
}
1513
export interface Modifier {
1614
constructor: ModifierConstructor & this;
1715
}
18-
export class Modifier {
16+
export class Modifier extends VersionableObject {
1917
preserve = true;
2018
get name(): string {
2119
return '';

packages/core/src/Modifiers.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { Modifier } from './Modifier';
22
import { Constructor, isConstructor } from '../../utils/src/utils';
3+
import { VersionableObject } from './Memory/VersionableObject';
4+
import { VersionableArray } from './Memory/VersionableArray';
35

4-
export class Modifiers {
6+
export class Modifiers extends VersionableObject {
57
private _contents: Modifier[];
68
constructor(...modifiers: Array<Modifier | Constructor<Modifier>>) {
9+
super();
710
const clonedModifiers = modifiers.map(mod => {
811
return mod instanceof Modifier ? mod.clone() : mod;
912
});
@@ -49,7 +52,7 @@ export class Modifiers {
4952
*/
5053
append(...modifiers: Array<Modifier | Constructor<Modifier>>): void {
5154
if (modifiers.length && !this._contents) {
52-
this._contents = [];
55+
this._contents = new VersionableArray();
5356
}
5457
for (const modifier of modifiers) {
5558
if (modifier instanceof Modifier) {
@@ -67,7 +70,7 @@ export class Modifiers {
6770
*/
6871
prepend(...modifiers: Array<Modifier | Constructor<Modifier>>): void {
6972
if (modifiers.length && !this._contents) {
70-
this._contents = [];
73+
this._contents = new VersionableArray();
7174
}
7275
for (const modifier of [...modifiers].reverse()) {
7376
if (modifier instanceof Modifier) {

0 commit comments

Comments
 (0)