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

Commit c03da81

Browse files
Gorashdmo-odoo
authored andcommitted
[IMP] Core: introduce modifier's level.
1 parent e96c442 commit c03da81

File tree

12 files changed

+458
-5
lines changed

12 files changed

+458
-5
lines changed

packages/core/src/Format.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ export class Format extends Modifier {
77
modifiers = new Modifiers();
88
constructor(htmlTag?: string) {
99
super();
10-
this.htmlTag = htmlTag;
10+
if (htmlTag) {
11+
this.htmlTag = htmlTag;
12+
}
1113
}
1214
get name(): string {
1315
return this.htmlTag.toLowerCase();

packages/core/src/Modifier.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ import { Constructor } from '../../utils/src/utils';
22
import { VNode } from './VNodes/VNode';
33
import { VersionableObject } from './Memory/VersionableObject';
44

5+
export enum ModifierLevel {
6+
LOW,
7+
MEDIUM,
8+
HIGH,
9+
}
510
export type ModifierTypeguard<T extends Modifier> = (modifier: Modifier) => modifier is T;
611
export type ModifierPredicate<T = Modifier | boolean> = T extends Modifier
712
? Constructor<T> | ModifierTypeguard<T>
@@ -17,6 +22,7 @@ export class Modifier extends VersionableObject {
1722
preserveAfterNode = true; // True to preserve modifier after the node that holds it.
1823
preserveAfterParagraphBreak = true; // True to preserve modifier after a paragraph break.
1924
preserveAfterLineBreak = true; // True to preserve modifier after a line break.
25+
level = ModifierLevel.MEDIUM;
2026

2127
get name(): string {
2228
return '';

packages/plugin-char/test/Char.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,8 @@ describePlugin(Char, testEditor => {
318318
await testEditor(BasicEditor, {
319319
contentBefore: '[]a',
320320
stepFunction: async (editor: JWEditor) => {
321-
await toggleFormat(editor, BoldFormat);
322321
await toggleFormat(editor, UnderlineFormat);
322+
await toggleFormat(editor, BoldFormat);
323323
await insertText(editor, 'b');
324324
},
325325
contentAfter: '<b><u>b[]</u></b>a',

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ export class DomLayoutEngine extends LayoutEngine {
195195
const cache = (this._rendererCache = await engine.render(
196196
updatedNodes,
197197
this._rendererCache,
198+
!!this._rendererCache,
198199
));
199200

200201
this._domReconciliationEngine.update(

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,6 +1534,10 @@ export class DomReconciliationEngine {
15341534
}
15351535
if (domNodes.length) {
15361536
if (domObject.tag) {
1537+
const swapNode = domNodes.find(domNode => domNode.contains(parentDomNode));
1538+
if (swapNode) {
1539+
parentDomNode.parentNode.removeChild(parentDomNode);
1540+
}
15371541
this._insertDomChildren(domNodes, parentDomNode, parentDomNode.firstChild);
15381542
} else {
15391543
newNode = true;

packages/plugin-inline/src/Inline.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ export class Inline<T extends JWPluginConfig = JWPluginConfig> extends JWPlugin<
9999
if (!this.cache.modifiers) {
100100
this.cache.modifiers = new Modifiers();
101101
}
102-
this.cache.modifiers.append(new FormatClass());
102+
this.cache.modifiers.prepend(FormatClass);
103103
}
104104
} else {
105105
const selectedInlines = range.selectedNodes(InlineNode);

packages/plugin-inline/test/Inline.test.ts

Lines changed: 297 additions & 1 deletion
Large diffs are not rendered by default.

packages/plugin-link/src/LinkFormat.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { Format } from '../../core/src/Format';
22
import { Attributes } from '../../plugin-xml/src/Attributes';
3+
import { ModifierLevel } from '../../core/src/Modifier';
34

45
export class LinkFormat extends Format {
56
preserveAfterParagraphBreak = false;
7+
level = ModifierLevel.HIGH;
68

79
constructor(url = '#', target = '') {
810
super('A');

packages/plugin-renderer-dom-object/src/DomObjectRenderingEngine.ts

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,8 +510,143 @@ export class DomObjectRenderingEngine extends RenderingEngine<DomObject> {
510510
renderingUnits.push(this._createUnit(cache, node, rendered));
511511
}
512512
}
513+
if (cache.optimizeModifiersRendering) {
514+
this._optimizeModifiersRendering(cache, renderingUnits);
515+
}
516+
513517
return renderingUnits;
514518
}
519+
/**
520+
* The modifiers will be sorted to be rendererd with the minimum of tag and keep the hight
521+
* modifier level.
522+
*
523+
* Eg: change this (without space of course):
524+
* <b>
525+
* <i>
526+
* _
527+
* <a href="#">__</a>
528+
* </i>
529+
* <a href="#">_</a>
530+
* <i>
531+
* <a href="#">__</a>
532+
* </i>
533+
* </b>
534+
* Into this to keep the link:
535+
* <b>
536+
* <i>_</i>
537+
* <a href="#">
538+
* <i>__</i>
539+
* _
540+
* <i>__</i>
541+
* </a>
542+
* </b>
543+
*
544+
* @param cache
545+
* @param renderingUnits
546+
*/
547+
private _optimizeModifiersRendering(
548+
cache: RenderingEngineCache<DomObject>,
549+
renderingUnits: RenderingBatchUnit[],
550+
): void {
551+
// Clone to use pop after and create the intervales.
552+
const unitToModifiers = new Map<RenderingBatchUnit, Modifier[]>();
553+
for (const renderingUnit of renderingUnits) {
554+
if (renderingUnit) {
555+
unitToModifiers.set(renderingUnit, [...renderingUnit[1]]);
556+
}
557+
}
558+
559+
// Group by same modifier, then order the modifiers and take care of the level.
560+
// Create modifiers interval (Modifier, level, index begin, index end).
561+
const intervals: [Modifier[], number, number, number][] = [];
562+
for (let i = 0; i < renderingUnits.length; i++) {
563+
const renderingUnit = renderingUnits[i];
564+
const unitModifiers = unitToModifiers.get(renderingUnit);
565+
if (unitModifiers) {
566+
while (unitModifiers.length) {
567+
// Take the first modifier and group.
568+
const modifierToSort = unitModifiers.pop();
569+
const level = modifierToSort.level;
570+
if (!level) {
571+
continue;
572+
}
573+
const modifiers: Modifier[] = [modifierToSort];
574+
const groupIndexes: number[] = [0];
575+
576+
let next: RenderingBatchUnit;
577+
let nextIndex = i + 1;
578+
while ((next = renderingUnits[nextIndex]) && unitToModifiers.get(next)) {
579+
const modifierIndex = unitToModifiers
580+
.get(next)
581+
.findIndex(modifier =>
582+
this._modifierIsSameAs(cache, modifierToSort, modifier),
583+
);
584+
if (modifierIndex === -1) {
585+
break;
586+
} else {
587+
const modifier = unitToModifiers.get(next)[modifierIndex];
588+
groupIndexes.push(next[1].indexOf(modifier));
589+
modifiers.push(modifier);
590+
unitToModifiers.get(next).splice(modifierIndex, 1);
591+
nextIndex++;
592+
}
593+
}
594+
intervals.push([modifiers, level, i, nextIndex - 1]);
595+
}
596+
}
597+
}
598+
599+
// Split interval if break an interval with greatest level.
600+
for (let i = 0; i < intervals.length; i++) {
601+
const self = intervals[i];
602+
// Use the same length because the newest are already splitted for this loop.
603+
const len = intervals.length;
604+
for (let u = 0; u < len; u++) {
605+
if (u === i) {
606+
continue;
607+
}
608+
const other = intervals[u];
609+
if (
610+
self[1] > other[1] ||
611+
(self[1] === other[1] && self[3] - self[2] > other[3] - other[2])
612+
) {
613+
// If greatest level or greatest number of VNodes split other modifiers.
614+
if (self[2] > other[2] && self[2] <= other[3] && self[3] > other[3]) {
615+
intervals.push([
616+
other[0].splice(0, other[3] - self[2] + 1),
617+
other[1],
618+
self[2],
619+
other[3],
620+
]);
621+
other[3] = self[2] - 1;
622+
} else if (self[3] >= other[2] && self[3] < other[3] && self[2] < other[2]) {
623+
intervals.push([
624+
other[0].splice(0, self[3] - other[2] + 1),
625+
other[1],
626+
other[2],
627+
self[3],
628+
]);
629+
other[2] = self[3] + 1;
630+
}
631+
}
632+
}
633+
}
634+
635+
// Sort by largest interval.
636+
intervals.sort((a, b) => a[3] - a[2] - b[3] + b[2]);
637+
638+
// Sort the modifiers in unit from the interval order.
639+
for (const interval of intervals) {
640+
const nodes = [];
641+
for (let i = interval[2]; i <= interval[3]; i++) {
642+
const modifer = interval[0][i - interval[2]];
643+
const modifiers = renderingUnits[i][1];
644+
modifiers.splice(modifiers.indexOf(modifer), 1);
645+
modifiers.unshift(modifer);
646+
nodes.push(renderingUnits[i][0]);
647+
}
648+
}
649+
}
515650
private _createUnit(
516651
cache: RenderingEngineCache<DomObject>,
517652
node: VNode,

packages/plugin-renderer/src/RenderingEngine.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ export class RenderingEngine<T = {}> {
7979
async render(
8080
nodes: VNode[],
8181
cache?: RenderingEngineCache<T>,
82+
optimizeModifiersRendering?: boolean,
8283
): Promise<RenderingEngineCache<T>> {
8384
if (!cache) {
8485
cache = new RenderingEngineCache();
@@ -95,6 +96,9 @@ export class RenderingEngine<T = {}> {
9596
},
9697
};
9798
}
99+
if (typeof optimizeModifiersRendering === 'boolean') {
100+
cache.optimizeModifiersRendering = optimizeModifiersRendering;
101+
}
98102

99103
const promises = this.renderBatched(
100104
cache,

0 commit comments

Comments
 (0)