Skip to content

Commit 091ae03

Browse files
committed
part 4
1 parent bfc921f commit 091ae03

File tree

7 files changed

+410
-281
lines changed

7 files changed

+410
-281
lines changed

package-lock.json

Lines changed: 6 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"canvas": "^3.1.0",
1010
"chai": "^4.2.0",
1111
"codemirror": "^6.0.1",
12-
"mitata": "^1.0.21",
12+
"mitata": "^1.0.34",
1313
"mocha": "^10.0.0",
1414
"peggy": "^2.0.1",
1515
"punycode": "^2.1.1",

src/layout-box.ts

Lines changed: 106 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type {Style} from './style.ts';
44
import type {Run} from './layout-text.ts';
55
import type {
66
InlineLevel,
7+
BlockLevel,
78
Break,
89
Inline,
910
BlockContainer,
@@ -106,21 +107,31 @@ export abstract class RenderItem {
106107

107108
log.text('\n');
108109

109-
if (
110-
this.isBlockContainerOfInlines() ||
111-
this.isBlockContainerOfBlocks() ||
112-
this.isInline()
113-
) {
114-
const children = this.isBlockContainerOfInlines() ? [this.root] : this.children;
110+
if (this.isBlockContainerOfBlocks()) {
115111
log.pushIndent();
116-
117-
for (let i = 0; i < children.length; i++) {
118-
children[i].log(options, log);
112+
for (let i = 0; i < this.children.length; i++) {
113+
this.children[i].log(options, log);
119114
}
120-
121115
log.popIndent();
122116
}
123117

118+
if (this.isBlockContainerOfInlines()) {
119+
const parents: Inline[] = [];
120+
for (let i = 0; i < this.tree.length; i++) {
121+
const child = this.tree[i];
122+
log.pushIndent();
123+
if (child.isInline()) {
124+
parents.push(child);
125+
}
126+
child.log(options, log);
127+
if (!child.isInline()) log.popIndent();
128+
while (parents.at(-1)?.lastDescendant === i) {
129+
log.popIndent();
130+
parents.pop();
131+
}
132+
}
133+
}
134+
124135
if (flush) log.flush();
125136
}
126137

@@ -761,11 +772,59 @@ export class BoxArea {
761772
}
762773
}
763774

775+
export class InlineStack {
776+
tree: InlineLevel[];
777+
parents: Box[];
778+
parentsStart: number;
779+
index: number;
780+
inlineStack: true;
781+
782+
constructor(ifc: BlockContainerOfInlines, parents: Box[], index = 0) {
783+
this.tree = ifc.tree;
784+
this.parents = parents;
785+
this.parentsStart = parents.length;
786+
this.index = index;
787+
this.inlineStack = true;
788+
}
789+
790+
getValue() {
791+
if (this.parentsStart < this.parents.length) {
792+
const parent = this.parents[this.parents.length - 1] as Inline;
793+
if (parent.lastDescendant === this.index - 1) return {sentinel: true as const};
794+
}
795+
796+
return this.index < this.tree.length ? this.tree[this.index] : undefined;
797+
}
798+
799+
lastChild() {
800+
const value = this.index < this.tree.length ? this.tree[this.index] : undefined;
801+
if (value?.isInline()) this.index = value.lastDescendant;
802+
}
803+
804+
next() {
805+
const item = this.getValue();
806+
if (item && !('sentinel' in item)) this.index++;
807+
return item;
808+
}
809+
}
810+
764811
const EmptyContainingBlock = new BoxArea(null!);
765812

813+
export function nextStack(stack: (BlockLevel | InlineStack | {sentinel: true})[]) {
814+
const value = stack[stack.length - 1];
815+
if ('inlineStack' in value) {
816+
const ret = value.next();
817+
if (ret === undefined) stack.pop();
818+
return ret;
819+
} else {
820+
stack.pop();
821+
return value;
822+
}
823+
}
824+
766825
export function prelayout(root: BlockContainer) {
767-
const stack: (InlineLevel | {sentinel: true})[] = [root];
768-
const parents: Box[] = [];
826+
const stack: (BlockLevel | InlineStack | {sentinel: true})[] = [root];
827+
const parents: (FormattingBox | Inline)[] = [];
769828
const ifcs: BlockContainerOfInlines[] = [];
770829
const pstack = [root.containingBlock];
771830
const bstack = [root.containingBlock];
@@ -775,10 +834,13 @@ export function prelayout(root: BlockContainer) {
775834
};
776835

777836
while (stack.length) {
778-
const box = stack.pop()!;
837+
const item = nextStack(stack);
838+
839+
if (item === undefined) continue; // end of inlines
779840

780-
if ('sentinel' in box) {
841+
if ('sentinel' in item) {
781842
const box = parents.pop()!;
843+
782844
if (box.isBlockContainerOfInlines()) ifcs.pop();
783845

784846
if (box.isBlockContainer()) {
@@ -791,61 +853,66 @@ export function prelayout(root: BlockContainer) {
791853
const parent = parents.at(-1);
792854
if (parent) box.propagate(parent);
793855
box.prelayoutPostorder(ctx);
794-
} else if (box.isBox()) {
795-
parents.push(box);
856+
} else if (item.isBox()) {
857+
const box = item;
796858
if (box.isBlockContainerOfInlines()) ifcs.push(box);
797859

798860
ctx.lastPositionedArea = pstack.at(-1)!;
799861
ctx.lastBlockContainerArea = bstack.at(-1)!;
800862

801-
stack.push({sentinel: true});
802863
box.prelayoutPreorder(ctx);
803864
if (box.isBlockContainer()) {
804865
bstack.push(box.getContentArea());
805866
if (box.style.position !== 'static') pstack.push(box.getPaddingArea());
806867
}
807868

808-
if (box.isBlockContainerOfBlocks() || box.isInline()) {
869+
if (box.isBlockContainerOfBlocks()) {
870+
parents.push(box);
871+
stack.push({sentinel: true});
809872
for (let i = box.children.length - 1; i >= 0; i--) {
810873
stack.push(box.children[i]);
811874
}
812875
} else if (box.isBlockContainerOfInlines()) {
813-
stack.push(box.root);
876+
parents.push(box);
877+
stack.push({sentinel: true}, new InlineStack(box, parents));
878+
} else if (box.isInline()) {
879+
parents.push(box);
880+
} else {
881+
item.propagate(parents.at(-1)!);
814882
}
815-
} else if (box.isRun()) {
816-
box.propagate(parents.at(-1)!, ifcs.at(-1)!.text);
883+
} else if (item.isRun()) {
884+
item.propagate(parents.at(-1)!, ifcs.at(-1)!.text);
817885
} else {
818-
box.propagate(parents.at(-1)!);
886+
item.propagate(parents.at(-1)!);
819887
}
820888
}
821889
}
822890

823891
export function postlayout(root: BlockContainer) {
824-
const stack: (BlockContainer | Inline | {sentinel: true})[] = [root];
892+
const stack: (BlockLevel | InlineStack | {sentinel: true})[] = [root];
825893
const parents: Box[] = [];
826894

827895
while (stack.length) {
828-
const box = stack.pop()!;
896+
const item = nextStack(stack);
897+
if (item === undefined) continue; // end inlines
829898

830-
if ('sentinel' in box) {
899+
if ('sentinel' in item) {
831900
const parent = parents.pop()!;
832901
parent.postlayoutPostorder();
833902
} else {
834-
box.postlayoutPreorder();
835-
stack.push({sentinel: true});
836-
parents.push(box);
837-
if (box.isBlockContainerOfBlocks() || box.isInline()) {
838-
for (let i = box.children.length - 1; i >= 0; i--) {
839-
const child = box.children[i];
840-
if (child.isBlockContainer() || child.isInline()) {
841-
stack.push(child);
842-
} else {
843-
child.postlayoutPreorder()
844-
child.postlayoutPostorder();
845-
}
903+
item.postlayoutPreorder();
904+
if (!item.isBox()) item.postlayoutPostorder();
905+
if (item.isBlockContainerOfBlocks()) {
906+
parents.push(item);
907+
stack.push({sentinel: true});
908+
for (let i = item.children.length - 1; i >= 0; i--) {
909+
stack.push(item.children[i]);
846910
}
847-
} else if (box.isBlockContainerOfInlines()) {
848-
stack.push(box.root);
911+
} else if (item.isBlockContainerOfInlines()) {
912+
parents.push(item);
913+
stack.push({sentinel: true}, new InlineStack(item, parents));
914+
} else if (item.isInline()) {
915+
parents.push(item);
849916
}
850917
}
851918
}

0 commit comments

Comments
 (0)