refactor IfBlock slightly

pull/1193/head
Rich Harris 8 years ago
parent c6bba03f1d
commit da155878a8

@ -101,7 +101,7 @@ export default class IfBlock extends Node {
? block.getUniqueName(`${name}_anchor`) ? block.getUniqueName(`${name}_anchor`)
: (this.next && this.next.var) || 'null'; : (this.next && this.next.var) || 'null';
const branches = getBranches(this.generator, block, parentNode, parentNodes, this); const branches = this.getBranches(block, parentNode, parentNodes, this);
const hasElse = isElseBranch(branches[branches.length - 1]); const hasElse = isElseBranch(branches[branches.length - 1]);
const if_name = hasElse ? '' : `if (${name}) `; const if_name = hasElse ? '' : `if (${name}) `;
@ -113,21 +113,12 @@ export default class IfBlock extends Node {
if (this.else) { if (this.else) {
if (hasOutros) { if (hasOutros) {
compoundWithOutros( this.buildCompoundWithOutros(block, parentNode, parentNodes, branches, dynamic, vars);
this.generator,
block,
parentNode,
parentNodes,
this,
branches,
dynamic,
vars
);
} else { } else {
compound(this.generator, block, parentNode, parentNodes, this, branches, dynamic, vars); this.buildCompound(block, parentNode, parentNodes, branches, dynamic, vars);
} }
} else { } else {
simple(this.generator, block, parentNode, parentNodes, this, branches[0], dynamic, vars); this.buildSimple(block, parentNode, parentNodes, branches[0], dynamic, vars);
} }
block.builders.create.addLine(`${if_name}${name}.c();`); block.builders.create.addLine(`${if_name}${name}.c();`);
@ -147,353 +138,334 @@ export default class IfBlock extends Node {
); );
} }
} }
}
buildCompound(
block: Block,
parentNode: string,
parentNodes: string,
branches,
dynamic,
{ name, anchor, hasElse, if_name }
) {
const select_block_type = this.generator.getUniqueName(`select_block_type`);
const current_block_type = block.getUniqueName(`current_block_type`);
const current_block_type_and = hasElse ? '' : `${current_block_type} && `;
this.generator.blocks.push(deindent`
function ${select_block_type}(state) {
${branches
.map(({ condition, block }) => `${condition ? `if (${condition}) ` : ''}return ${block};`)
.join('\n')}
}
`);
block.builders.init.addBlock(deindent`
var ${current_block_type} = ${select_block_type}(state);
var ${name} = ${current_block_type_and}${current_block_type}(#component, state);
`);
const mountOrIntro = branches[0].hasIntroMethod ? 'i' : 'm';
// TODO move all this into the class const initialMountNode = parentNode || '#target';
const anchorNode = parentNode ? 'null' : 'anchor';
block.builders.mount.addLine(
`${if_name}${name}.${mountOrIntro}(${initialMountNode}, ${anchorNode});`
);
function getBranches( const updateMountNode = this.getUpdateMountNode(anchor);
generator: DomGenerator,
block: Block,
parentNode: string,
parentNodes: string,
node: Node
) {
block.contextualise(node.expression); // TODO remove
const branches = [ const changeBlock = deindent`
{ ${hasElse
condition: node.metadata.snippet, ? deindent`
block: node.block.name, ${name}.u();
hasUpdateMethod: node.block.hasUpdateMethod, ${name}.d();
hasIntroMethod: node.block.hasIntroMethod, `
hasOutroMethod: node.block.hasOutroMethod, : deindent`
}, if (${name}) {
]; ${name}.u();
${name}.d();
}`}
${name} = ${current_block_type_and}${current_block_type}(#component, state);
${if_name}${name}.c();
${if_name}${name}.${mountOrIntro}(${updateMountNode}, ${anchor});
`;
visitChildren(generator, block, node); if (dynamic) {
block.builders.update.addBlock(deindent`
if (${current_block_type} === (${current_block_type} = ${select_block_type}(state)) && ${name}) {
${name}.p(changed, state);
} else {
${changeBlock}
}
`);
} else {
block.builders.update.addBlock(deindent`
if (${current_block_type} !== (${current_block_type} = ${select_block_type}(state))) {
${changeBlock}
}
`);
}
if (isElseIf(node.else)) { block.builders.unmount.addLine(`${if_name}${name}.u();`);
branches.push(
...getBranches(generator, block, parentNode, parentNodes, node.else.children[0])
);
} else {
branches.push({
condition: null,
block: node.else ? node.else.block.name : null,
hasUpdateMethod: node.else ? node.else.block.hasUpdateMethod : false,
hasIntroMethod: node.else ? node.else.block.hasIntroMethod : false,
hasOutroMethod: node.else ? node.else.block.hasOutroMethod : false,
});
if (node.else) { block.builders.destroy.addLine(`${if_name}${name}.d();`);
visitChildren(generator, block, node.else);
}
} }
return branches; // if any of the siblings have outros, we need to keep references to the blocks
} // (TODO does this only apply to bidi transitions?)
buildCompoundWithOutros(
block: Block,
parentNode: string,
parentNodes: string,
branches,
dynamic,
{ name, anchor, hasElse }
) {
const select_block_type = block.getUniqueName(`select_block_type`);
const current_block_type_index = block.getUniqueName(`current_block_type_index`);
const previous_block_index = block.getUniqueName(`previous_block_index`);
const if_block_creators = block.getUniqueName(`if_block_creators`);
const if_blocks = block.getUniqueName(`if_blocks`);
function visitChildren( const if_current_block_type_index = hasElse
generator: DomGenerator, ? ''
block: Block, : `if (~${current_block_type_index}) `;
node: Node
) {
node.children.forEach((child: Node) => {
child.build(node.block, null, 'nodes');
});
}
block.addVariable(current_block_type_index);
block.addVariable(name);
block.builders.init.addBlock(deindent`
var ${if_block_creators} = [
${branches.map(branch => branch.block).join(',\n')}
];
function simple( var ${if_blocks} = [];
generator: DomGenerator,
block: Block,
parentNode: string,
parentNodes: string,
node: Node,
branch,
dynamic,
{ name, anchor, if_name }
) {
block.builders.init.addBlock(deindent`
var ${name} = (${branch.condition}) && ${branch.block}(#component, state);
`);
const mountOrIntro = branch.hasIntroMethod ? 'i' : 'm'; function ${select_block_type}(state) {
const initialMountNode = parentNode || '#target'; ${branches
const anchorNode = parentNode ? 'null' : 'anchor'; .map(({ condition, block }, i) => `${condition ? `if (${condition}) ` : ''}return ${block ? i : -1};`)
.join('\n')}
}
`);
block.builders.mount.addLine( if (hasElse) {
`if (${name}) ${name}.${mountOrIntro}(${initialMountNode}, ${anchorNode});` block.builders.init.addBlock(deindent`
); ${current_block_type_index} = ${select_block_type}(state);
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#component, state);
`);
} else {
block.builders.init.addBlock(deindent`
if (~(${current_block_type_index} = ${select_block_type}(state))) {
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#component, state);
}
`);
}
const updateMountNode = node.getUpdateMountNode(anchor); const mountOrIntro = branches[0].hasIntroMethod ? 'i' : 'm';
const initialMountNode = parentNode || '#target';
const anchorNode = parentNode ? 'null' : 'anchor';
const enter = dynamic block.builders.mount.addLine(
? branch.hasIntroMethod `${if_current_block_type_index}${if_blocks}[${current_block_type_index}].${mountOrIntro}(${initialMountNode}, ${anchorNode});`
);
const updateMountNode = this.getUpdateMountNode(anchor);
const destroyOldBlock = deindent`
${name}.o(function() {
${if_blocks}[ ${previous_block_index} ].u();
${if_blocks}[ ${previous_block_index} ].d();
${if_blocks}[ ${previous_block_index} ] = null;
});
`;
const createNewBlock = deindent`
${name} = ${if_blocks}[${current_block_type_index}];
if (!${name}) {
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#component, state);
${name}.c();
}
${name}.${mountOrIntro}(${updateMountNode}, ${anchor});
`;
const changeBlock = hasElse
? deindent` ? deindent`
if (${name}) { ${destroyOldBlock}
${name}.p(changed, state);
} else {
${name} = ${branch.block}(#component, state);
if (${name}) ${name}.c();
}
${name}.i(${updateMountNode}, ${anchor}); ${createNewBlock}
` `
: deindent` : deindent`
if (${name}) { if (${name}) {
${name}.p(changed, state); ${destroyOldBlock}
} else {
${name} = ${branch.block}(#component, state);
${name}.c();
${name}.m(${updateMountNode}, ${anchor});
} }
`
: branch.hasIntroMethod if (~${current_block_type_index}) {
? deindent` ${createNewBlock}
if (!${name}) { } else {
${name} = ${branch.block}(#component, state); ${name} = null;
${name}.c();
}
${name}.i(${updateMountNode}, ${anchor});
`
: deindent`
if (!${name}) {
${name} = ${branch.block}(#component, state);
${name}.c();
${name}.m(${updateMountNode}, ${anchor});
} }
`; `;
// no `p()` here — we don't want to update outroing nodes, if (dynamic) {
// as that will typically result in glitching block.builders.update.addBlock(deindent`
const exit = branch.hasOutroMethod var ${previous_block_index} = ${current_block_type_index};
? deindent` ${current_block_type_index} = ${select_block_type}(state);
${name}.o(function() { if (${current_block_type_index} === ${previous_block_index}) {
${name}.u(); ${if_current_block_type_index}${if_blocks}[${current_block_type_index}].p(changed, state);
${name}.d(); } else {
${name} = null; ${changeBlock}
}); }
` `);
: deindent` } else {
${name}.u(); block.builders.update.addBlock(deindent`
${name}.d(); var ${previous_block_index} = ${current_block_type_index};
${name} = null; ${current_block_type_index} = ${select_block_type}(state);
`; if (${current_block_type_index} !== ${previous_block_index}) {
${changeBlock}
block.builders.update.addBlock(deindent` }
if (${branch.condition}) { `);
${enter}
} else if (${name}) {
${exit}
} }
`);
block.builders.unmount.addLine(`${if_name}${name}.u();`); block.builders.destroy.addLine(deindent`
${if_current_block_type_index}{
block.builders.destroy.addLine(`${if_name}${name}.d();`); ${if_blocks}[${current_block_type_index}].u();
} ${if_blocks}[${current_block_type_index}].d();
}
`);
}
function compound( buildSimple(
generator: DomGenerator, block: Block,
block: Block, parentNode: string,
parentNode: string,
parentNodes: string, parentNodes: string,
node: Node, branch,
branches, dynamic,
dynamic, { name, anchor, if_name }
{ name, anchor, hasElse, if_name } ) {
) { block.builders.init.addBlock(deindent`
const select_block_type = generator.getUniqueName(`select_block_type`); var ${name} = (${branch.condition}) && ${branch.block}(#component, state);
const current_block_type = block.getUniqueName(`current_block_type`); `);
const current_block_type_and = hasElse ? '' : `${current_block_type} && `;
generator.blocks.push(deindent`
function ${select_block_type}(state) {
${branches
.map(({ condition, block }) => `${condition ? `if (${condition}) ` : ''}return ${block};`)
.join('\n')}
}
`);
block.builders.init.addBlock(deindent`
var ${current_block_type} = ${select_block_type}(state);
var ${name} = ${current_block_type_and}${current_block_type}(#component, state);
`);
const mountOrIntro = branches[0].hasIntroMethod ? 'i' : 'm';
const initialMountNode = parentNode || '#target'; const mountOrIntro = branch.hasIntroMethod ? 'i' : 'm';
const anchorNode = parentNode ? 'null' : 'anchor'; const initialMountNode = parentNode || '#target';
block.builders.mount.addLine( const anchorNode = parentNode ? 'null' : 'anchor';
`${if_name}${name}.${mountOrIntro}(${initialMountNode}, ${anchorNode});`
);
const updateMountNode = node.getUpdateMountNode(anchor); block.builders.mount.addLine(
`if (${name}) ${name}.${mountOrIntro}(${initialMountNode}, ${anchorNode});`
);
const changeBlock = deindent` const updateMountNode = this.getUpdateMountNode(anchor);
${hasElse
const enter = dynamic
? branch.hasIntroMethod
? deindent`
if (${name}) {
${name}.p(changed, state);
} else {
${name} = ${branch.block}(#component, state);
if (${name}) ${name}.c();
}
${name}.i(${updateMountNode}, ${anchor});
`
: deindent`
if (${name}) {
${name}.p(changed, state);
} else {
${name} = ${branch.block}(#component, state);
${name}.c();
${name}.m(${updateMountNode}, ${anchor});
}
`
: branch.hasIntroMethod
? deindent`
if (!${name}) {
${name} = ${branch.block}(#component, state);
${name}.c();
}
${name}.i(${updateMountNode}, ${anchor});
`
: deindent`
if (!${name}) {
${name} = ${branch.block}(#component, state);
${name}.c();
${name}.m(${updateMountNode}, ${anchor});
}
`;
// no `p()` here — we don't want to update outroing nodes,
// as that will typically result in glitching
const exit = branch.hasOutroMethod
? deindent` ? deindent`
${name}.u(); ${name}.o(function() {
${name}.d();
`
: deindent`
if (${name}) {
${name}.u(); ${name}.u();
${name}.d(); ${name}.d();
}`} ${name} = null;
${name} = ${current_block_type_and}${current_block_type}(#component, state); });
${if_name}${name}.c(); `
${if_name}${name}.${mountOrIntro}(${updateMountNode}, ${anchor}); : deindent`
`; ${name}.u();
${name}.d();
${name} = null;
`;
if (dynamic) {
block.builders.update.addBlock(deindent` block.builders.update.addBlock(deindent`
if (${current_block_type} === (${current_block_type} = ${select_block_type}(state)) && ${name}) { if (${branch.condition}) {
${name}.p(changed, state); ${enter}
} else { } else if (${name}) {
${changeBlock} ${exit}
} }
`); `);
} else {
block.builders.update.addBlock(deindent`
if (${current_block_type} !== (${current_block_type} = ${select_block_type}(state))) {
${changeBlock}
}
`);
}
block.builders.unmount.addLine(`${if_name}${name}.u();`); block.builders.unmount.addLine(`${if_name}${name}.u();`);
block.builders.destroy.addLine(`${if_name}${name}.d();`); block.builders.destroy.addLine(`${if_name}${name}.d();`);
} }
// if any of the siblings have outros, we need to keep references to the blocks getBranches(
// (TODO does this only apply to bidi transitions?) block: Block,
function compoundWithOutros( parentNode: string,
generator: DomGenerator, parentNodes: string,
block: Block, node: Node
parentNode: string, ) {
parentNodes: string, block.contextualise(node.expression); // TODO remove
node: Node,
branches, const branches = [
dynamic, {
{ name, anchor, hasElse } condition: node.metadata.snippet,
) { block: node.block.name,
const select_block_type = block.getUniqueName(`select_block_type`); hasUpdateMethod: node.block.hasUpdateMethod,
const current_block_type_index = block.getUniqueName(`current_block_type_index`); hasIntroMethod: node.block.hasIntroMethod,
const previous_block_index = block.getUniqueName(`previous_block_index`); hasOutroMethod: node.block.hasOutroMethod,
const if_block_creators = block.getUniqueName(`if_block_creators`); },
const if_blocks = block.getUniqueName(`if_blocks`);
const if_current_block_type_index = hasElse
? ''
: `if (~${current_block_type_index}) `;
block.addVariable(current_block_type_index);
block.addVariable(name);
block.builders.init.addBlock(deindent`
var ${if_block_creators} = [
${branches.map(branch => branch.block).join(',\n')}
]; ];
var ${if_blocks} = []; this.visitChildren(block, node);
function ${select_block_type}(state) { if (isElseIf(node.else)) {
${branches branches.push(
.map(({ condition, block }, i) => `${condition ? `if (${condition}) ` : ''}return ${block ? i : -1};`) ...this.getBranches(block, parentNode, parentNodes, node.else.children[0])
.join('\n')} );
} } else {
`); branches.push({
condition: null,
block: node.else ? node.else.block.name : null,
hasUpdateMethod: node.else ? node.else.block.hasUpdateMethod : false,
hasIntroMethod: node.else ? node.else.block.hasIntroMethod : false,
hasOutroMethod: node.else ? node.else.block.hasOutroMethod : false,
});
if (hasElse) { if (node.else) {
block.builders.init.addBlock(deindent` this.visitChildren(block, node.else);
${current_block_type_index} = ${select_block_type}(state);
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#component, state);
`);
} else {
block.builders.init.addBlock(deindent`
if (~(${current_block_type_index} = ${select_block_type}(state))) {
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#component, state);
} }
`);
}
const mountOrIntro = branches[0].hasIntroMethod ? 'i' : 'm';
const initialMountNode = parentNode || '#target';
const anchorNode = parentNode ? 'null' : 'anchor';
block.builders.mount.addLine(
`${if_current_block_type_index}${if_blocks}[${current_block_type_index}].${mountOrIntro}(${initialMountNode}, ${anchorNode});`
);
const updateMountNode = node.getUpdateMountNode(anchor);
const destroyOldBlock = deindent`
${name}.o(function() {
${if_blocks}[ ${previous_block_index} ].u();
${if_blocks}[ ${previous_block_index} ].d();
${if_blocks}[ ${previous_block_index} ] = null;
});
`;
const createNewBlock = deindent`
${name} = ${if_blocks}[${current_block_type_index}];
if (!${name}) {
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#component, state);
${name}.c();
} }
${name}.${mountOrIntro}(${updateMountNode}, ${anchor});
`;
const changeBlock = hasElse return branches;
? deindent`
${destroyOldBlock}
${createNewBlock}
`
: deindent`
if (${name}) {
${destroyOldBlock}
}
if (~${current_block_type_index}) {
${createNewBlock}
} else {
${name} = null;
}
`;
if (dynamic) {
block.builders.update.addBlock(deindent`
var ${previous_block_index} = ${current_block_type_index};
${current_block_type_index} = ${select_block_type}(state);
if (${current_block_type_index} === ${previous_block_index}) {
${if_current_block_type_index}${if_blocks}[${current_block_type_index}].p(changed, state);
} else {
${changeBlock}
}
`);
} else {
block.builders.update.addBlock(deindent`
var ${previous_block_index} = ${current_block_type_index};
${current_block_type_index} = ${select_block_type}(state);
if (${current_block_type_index} !== ${previous_block_index}) {
${changeBlock}
}
`);
} }
block.builders.destroy.addLine(deindent` visitChildren(block: Block, node: Node) {
${if_current_block_type_index}{ node.children.forEach((child: Node) => {
${if_blocks}[${current_block_type_index}].u(); child.build(node.block, null, 'nodes');
${if_blocks}[${current_block_type_index}].d(); });
} }
`);
} }
Loading…
Cancel
Save