From 3e40ce6e7f407f98330e035106dcd193244438c8 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Sun, 20 Sep 2020 22:53:11 +0800 Subject: [PATCH] clean up --- src/compiler/compile/css/Selector.ts | 142 +++++++++++++++------------ 1 file changed, 78 insertions(+), 64 deletions(-) diff --git a/src/compiler/compile/css/Selector.ts b/src/compiler/compile/css/Selector.ts index 611ce5b724..c141f6b481 100644 --- a/src/compiler/compile/css/Selector.ts +++ b/src/compiler/compile/css/Selector.ts @@ -15,12 +15,8 @@ enum BlockAppliesToNode { UnknownSelectorType } enum NodeExist { - Probably, - Definitely, -} -interface ElementAndExist { - element: Element; - exist: NodeExist; + Probably = 1, + Definitely = 2, } const whitelist_attribute_selector = new Map([ @@ -212,21 +208,11 @@ function apply_selector(blocks: Block[], node: Element, to_encapsulate: any[]): } return false; - } else if (block.combinator.name === '+') { - const siblings = get_possible_element_siblings(node, true); - let has_match = false; - for (const possible_sibling of siblings) { - if (apply_selector(blocks.slice(), possible_sibling.element, to_encapsulate)) { - to_encapsulate.push({ node, block }); - has_match = true; - } - } - return has_match; - } else if (block.combinator.name === '~') { - const siblings = get_possible_element_siblings(node, false); + } else if (block.combinator.name === '+' || block.combinator.name === '~') { + const siblings = get_possible_element_siblings(node, block.combinator.name === '+'); let has_match = false; - for (const possible_sibling of siblings) { - if (apply_selector(blocks.slice(), possible_sibling.element, to_encapsulate)) { + for (const possible_sibling of siblings.keys()) { + if (apply_selector(blocks.slice(), possible_sibling, to_encapsulate)) { to_encapsulate.push({ node, block }); has_match = true; } @@ -415,13 +401,13 @@ function get_element_parent(node: Element): Element | null { return parent as Element | null; } -function get_possible_element_siblings(node: INode, adjacent_only: boolean): ElementAndExist[] { - const result: ElementAndExist[] = []; +function get_possible_element_siblings(node: INode, adjacent_only: boolean): Map { + const result: Map = new Map(); let prev: INode = node; while (prev = prev.prev) { if (prev.type === 'Element') { if (!prev.attributes.find(attr => attr.name.toLowerCase() === 'slot')) { - result.push({ element: prev as Element, exist: NodeExist.Definitely }); + result.set(prev, NodeExist.Definitely); } if (adjacent_only) { @@ -429,8 +415,9 @@ function get_possible_element_siblings(node: INode, adjacent_only: boolean): Ele } } else if (prev.type === 'EachBlock' || prev.type === 'IfBlock' || prev.type === 'AwaitBlock') { const possible_last_child = get_possible_last_child(prev, adjacent_only); - result.push(...possible_last_child); - if (adjacent_only && possible_last_child.find(child => child.exist === NodeExist.Definitely)) { + + add_to_map(possible_last_child, result); + if (adjacent_only && has_definite_elements(possible_last_child)) { return result; } } @@ -438,23 +425,24 @@ function get_possible_element_siblings(node: INode, adjacent_only: boolean): Ele if (!prev || !adjacent_only) { let parent: INode = node; - if (node.type === 'ElseBlock') { - parent = parent.parent; - } + let skip_each_for_last_child = node.type === 'ElseBlock'; while ((parent = parent.parent) && (parent.type === 'EachBlock' || parent.type === 'IfBlock' || parent.type === 'ElseBlock' || parent.type === 'AwaitBlock')) { const possible_siblings = get_possible_element_siblings(parent, adjacent_only); - result.push(...possible_siblings); + add_to_map(possible_siblings, result); if (parent.type === 'EachBlock') { // first child of each block can select the last child of each block as previous sibling - for (const each_parent_last_child of get_possible_last_child(parent, adjacent_only)) { - result.push(each_parent_last_child); + if (skip_each_for_last_child) { + skip_each_for_last_child = false; + } else { + add_to_map(get_possible_last_child(parent, adjacent_only), result); } } else if (parent.type === 'ElseBlock') { + skip_each_for_last_child = true; parent = parent.parent; } - if (adjacent_only && possible_siblings.find(sibling => sibling.exist === NodeExist.Definitely)) { + if (adjacent_only && has_definite_elements(possible_siblings)) { break; } } @@ -463,69 +451,95 @@ function get_possible_element_siblings(node: INode, adjacent_only: boolean): Ele return result; } -function get_possible_last_child(block: EachBlock | IfBlock | AwaitBlock, adjacent_only: boolean): ElementAndExist[] { - const result = []; +function get_possible_last_child(block: EachBlock | IfBlock | AwaitBlock, adjacent_only: boolean): Map { + const result: Map = new Map(); if (block.type === 'EachBlock') { - const each_result: ElementAndExist[] = loop_child(block.children, adjacent_only); - const else_result: ElementAndExist[] = block.else ? loop_child(block.else.children, adjacent_only) : []; + const each_result: Map = loop_child(block.children, adjacent_only); + const else_result: Map = block.else ? loop_child(block.else.children, adjacent_only) : new Map(); - const not_exhaustive = - else_result.length === 0 || !else_result.find(result => result.exist === NodeExist.Definitely); + const not_exhaustive = !has_definite_elements(else_result); if (not_exhaustive) { - each_result.forEach(result => result.exist = NodeExist.Probably); - else_result.forEach(result => result.exist = NodeExist.Probably); + mark_as_probably(each_result); + mark_as_probably(else_result); } - result.push(...each_result, ...else_result); + add_to_map(each_result, result); + add_to_map(else_result, result); } else if (block.type === 'IfBlock') { - const if_result: ElementAndExist[] = loop_child(block.children, adjacent_only); - const else_result: ElementAndExist[] = block.else ? loop_child(block.else.children, adjacent_only) : []; + const if_result: Map = loop_child(block.children, adjacent_only); + const else_result: Map = block.else ? loop_child(block.else.children, adjacent_only) : new Map(); - const not_exhaustive = - if_result.length === 0 || !if_result.find(result => result.exist === NodeExist.Definitely) || - else_result.length === 0 || !else_result.find(result => result.exist === NodeExist.Definitely); + const not_exhaustive = !has_definite_elements(if_result) || !has_definite_elements(else_result); if (not_exhaustive) { - if_result.forEach(result => result.exist = NodeExist.Probably); - else_result.forEach(result => result.exist = NodeExist.Probably); + mark_as_probably(if_result); + mark_as_probably(else_result); } - result.push(...if_result, ...else_result); + add_to_map(if_result, result); + add_to_map(else_result, result); } else if (block.type === 'AwaitBlock') { - const pending_result: ElementAndExist[] = block.pending ? loop_child(block.pending.children, adjacent_only) : []; - const then_result: ElementAndExist[] = block.then ? loop_child(block.then.children, adjacent_only) : []; - const catch_result: ElementAndExist[] = block.catch ? loop_child(block.catch.children, adjacent_only) : []; + const pending_result: Map = block.pending ? loop_child(block.pending.children, adjacent_only) : new Map(); + const then_result: Map = block.then ? loop_child(block.then.children, adjacent_only) : new Map(); + const catch_result: Map = block.catch ? loop_child(block.catch.children, adjacent_only) : new Map(); - const not_exhaustive = - pending_result.length === 0 || !pending_result.find(result => result.exist === NodeExist.Definitely) || - then_result.length === 0 || !then_result.find(result => result.exist === NodeExist.Definitely) || - catch_result.length === 0 || !catch_result.find(result => result.exist === NodeExist.Definitely); + const not_exhaustive = !has_definite_elements(pending_result) || !has_definite_elements(then_result) || !has_definite_elements(catch_result); if (not_exhaustive) { - pending_result.forEach(result => result.exist = NodeExist.Probably); - then_result.forEach(result => result.exist = NodeExist.Probably); - catch_result.forEach(result => result.exist = NodeExist.Probably); + mark_as_probably(pending_result); + mark_as_probably(then_result); + mark_as_probably(catch_result); } - result.push(...pending_result,...then_result,...catch_result); + + add_to_map(pending_result, result); + add_to_map(then_result, result); + add_to_map(catch_result, result); } return result; } +function has_definite_elements(result: Map): boolean { + if (result.size === 0) return false; + for (const exist of result.values()) { + if (exist === NodeExist.Definitely) { + return true; + } + } + return false; +} + +function add_to_map(from: Map, to: Map) { + from.forEach((exist, element) => { + to.set(element, higher_existance(exist, to.get(element))); + }); +} + +function higher_existance(exist1: NodeExist | null, exist2: NodeExist | null): NodeExist { + if (exist1 === undefined || exist2 === undefined) return exist1 || exist2; + return exist1 > exist2 ? exist1 : exist2; +} + +function mark_as_probably(result: Map) { + for (const key of result.keys()) { + result.set(key, NodeExist.Probably); + } +} + function loop_child(children: INode[], adjacent_only: boolean) { - const result = []; + const result: Map = new Map(); for (let i = children.length - 1; i >= 0; i--) { const child = children[i]; if (child.type === 'Element') { - result.push({ element: child, exist: NodeExist.Definitely }); + result.set(child, NodeExist.Definitely); if (adjacent_only) { break; } } else if (child.type === 'EachBlock' || child.type === 'IfBlock' || child.type === 'AwaitBlock') { const child_result = get_possible_last_child(child, adjacent_only); - result.push(...child_result); - if (adjacent_only && child_result.find(child => child.exist === NodeExist.Definitely)) { + add_to_map(child_result, result); + if (adjacent_only && has_definite_elements(child_result)) { break; } }