(p: &mut Parser<'a, I>)
| 137 | |
| 138 | impl<'a> Parse<'a> for QueryCompoundSelector<'a> { |
| 139 | fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self> |
| 140 | where |
| 141 | I: Iterator<Item = Cursor> + Clone, |
| 142 | { |
| 143 | let mut parts = Vec::new_in(p.bump()); |
| 144 | let mut metadata = QuerySelectorMetadata::default(); |
| 145 | |
| 146 | // Build segments in forward order. Each segment stores the combinator that follows it. |
| 147 | let mut segments: SmallVec<[SelectorSegment; 4]> = SmallVec::new(); |
| 148 | let mut segment_start = 0u16; |
| 149 | |
| 150 | // Track rightmost segment's type for rightmost_type_id computation |
| 151 | let mut rightmost_type: Option<NodeId> = None; |
| 152 | let mut rightmost_has_wildcard = false; |
| 153 | |
| 154 | // Track leading combinator (for :has() inner selectors) |
| 155 | let mut leading_combinator: Option<QueryCombinator> = None; |
| 156 | |
| 157 | // Trim leading whitespace |
| 158 | p.consume_trivia(); |
| 159 | |
| 160 | // Parse components incrementally, building metadata and segments as we go |
| 161 | while let Some(component) = Self::parse_compound_selector_part(p)? { |
| 162 | // Handle combinators separately - don't add to parts |
| 163 | if let Some(combinator) = component.as_combinator() { |
| 164 | let segment_end = parts.len() as u16; |
| 165 | if segment_start < segment_end { |
| 166 | // Emit the segment that just ended, with this combinator following it |
| 167 | segments.push(SelectorSegment { |
| 168 | start: segment_start, |
| 169 | end: segment_end, |
| 170 | combinator: Some(combinator), |
| 171 | }); |
| 172 | segment_start = segment_end; |
| 173 | rightmost_type = None; |
| 174 | rightmost_has_wildcard = false; |
| 175 | } else if segments.is_empty() && leading_combinator.is_none() { |
| 176 | leading_combinator = Some(combinator); |
| 177 | } |
| 178 | metadata = metadata.merge(component.self_metadata()); |
| 179 | continue; |
| 180 | } |
| 181 | |
| 182 | // Track type/wildcard for rightmost_type_id computation |
| 183 | match &component { |
| 184 | QuerySelectorComponent::Type(t) => { |
| 185 | let c: Cursor = t.0.into(); |
| 186 | let node_id = CsskitAtomSet::from_bits(c.atom_bits()).to_node_id(); |
| 187 | if rightmost_type.is_none() { |
| 188 | rightmost_type = node_id; |
| 189 | } |
| 190 | // Mark selector as invalid if type is unknown |
| 191 | if node_id.is_none() { |
| 192 | metadata.is_invalid = true; |
| 193 | } |
| 194 | // Accumulate at-rule filter for all type selectors in the compound selector |
| 195 | if let Some(at_rule_id) = node_id.and_then(|id| id.to_at_rule_id()) { |
| 196 | metadata.at_rule_filter |= at_rule_id; |
no test coverage detected