fn populate_state()

in cli/src/generate/properties.rs [245:403]


    fn populate_state(&mut self, item_set: ItemSet<'a>, state_id: StateId) {
        let is_start_state = state_id == 0;
        let mut transitions: HashMap<PropertyTransitionJSON, u32> = HashMap::new();
        let mut selector_matches = Vec::new();

        // First, compute all of the possible state transition conditions for
        // this state, and all of the rules that are currently matching.
        for item in item_set.0.iter().chain(self.start_item_set.0.iter()) {
            // If this item has more elements remaining in its selector, then
            // add a state transition based on the next step.
            if let Some(step) = item.next_step() {
                transitions
                    .entry(PropertyTransitionJSON {
                        kind: step.kind.clone(),
                        field: step.field.clone(),
                        named: step.is_named,
                        index: step.child_index,
                        text: step.text_pattern.clone(),
                        state_id: 0,
                    })
                    .and_modify(|rule_id| {
                        if item.rule_id > *rule_id {
                            *rule_id = item.rule_id;
                        }
                    })
                    .or_insert(item.rule_id);
            }
            // If the item has matched its entire selector, then the item's
            // properties are applicable to this state.
            else {
                selector_matches.push(SelectorMatch {
                    rule_id: item.rule_id,
                    specificity: selector_specificity(item.selector),
                });
            }
        }

        // Compute the merged properties that apply in the current state.
        // Sort the matching property sets by ascending specificity and by
        // their order in the sheet. This way, more specific selectors and later
        // rules will override less specific selectors and earlier rules.
        let mut properties = PropertySet::new();
        selector_matches.sort_unstable_by(|a, b| {
            (a.specificity.cmp(&b.specificity)).then_with(|| a.rule_id.cmp(&b.rule_id))
        });
        selector_matches.dedup();
        for selector_match in selector_matches {
            let rule = &self.rules[selector_match.rule_id as usize];
            for (property, value) in &rule.properties {
                properties.insert(property.clone(), value.clone());
            }
        }
        self.output.states[state_id].property_set_id = self.add_property_set(properties);

        // If there are multiple transitions that could *both* match (e.g. one based on a
        // a node type and one based on a field name), then create an additional transition
        // for the intersection of the two.
        let mut i = 0;
        let mut transition_list = transitions.into_iter().collect::<Vec<_>>();
        while i < transition_list.len() {
            for j in 0..i {
                if let Some(intersection) =
                    self.intersect_transitions(&transition_list[j].0, &transition_list[i].0)
                {
                    transition_list.push((
                        intersection,
                        u32::max(transition_list[i].1, transition_list[j].1),
                    ));
                }
            }
            i += 1;
        }

        // Ensure that for a given node type, more specific transitions are tried
        // first, and in the event of a tie, transitions corresponding to later rules
        // in the cascade are tried first. Also, sort the non-intersecting transitions
        // by name to guarantee a deterministic order.
        transition_list.sort_by(|a, b| {
            (transition_specificity(&b.0).cmp(&transition_specificity(&a.0)))
                .then_with(|| b.1.cmp(&a.1))
                .then_with(|| a.0.kind.cmp(&b.0.kind))
                .then_with(|| a.0.named.cmp(&b.0.named))
                .then_with(|| a.0.field.cmp(&b.0.field))
        });

        // For eacy possible state transition, compute the set of items in that transition's
        // destination state.
        i = 0;
        while i < transition_list.len() {
            let transition = &mut transition_list[i].0;
            let transition_is_leaf = transition.named == Some(false)
                || transition
                    .kind
                    .as_ref()
                    .map_or(false, |kind| self.token_names.contains(kind));

            let mut next_item_set = ItemSet::new();
            let mut transition_differs_from_start_state = false;
            for item in item_set.0.iter().chain(self.start_item_set.0.iter()) {
                if let Some(next_step) = item.next_step() {
                    // If the next step of the item's selector satisfies this transition,
                    // advance the item to the next part of its selector and add the
                    // resulting item to this transition's destination state.
                    if step_matches_transition(next_step, transition) {
                        let next_item = Item {
                            rule_id: item.rule_id,
                            selector: item.selector,
                            step_id: item.step_id + 1,
                        };
                        if !transition_is_leaf || next_item.is_done() {
                            next_item_set.insert(next_item);
                            if item.step_id > 0 {
                                transition_differs_from_start_state = true;
                            }
                        }
                    }

                    // If the next step of the item is not an immediate child, then
                    // include this item in this transition's destination state, because
                    // the next step of the item might match a descendant node.
                    if !transition_is_leaf && !next_step.is_immediate && item.step_id > 0 {
                        next_item_set.insert(*item);
                        transition_differs_from_start_state = true;
                    }
                }
            }

            if (is_start_state || transition_differs_from_start_state)
                && !next_item_set.0.is_empty()
            {
                transition.state_id = self.add_state(next_item_set);
                if is_start_state || !self.output.states[0].transitions.contains(&transition) {
                    i += 1;
                    continue;
                }
            }
            transition_list.remove(i);
        }

        self.output.states[state_id]
            .transitions
            .extend(transition_list.into_iter().map(|i| i.0));

        // Compute the default successor item set - the item set that
        // we should advance to if the next element doesn't match any
        // of the next elements in the item set's selectors.
        let mut default_next_item_set = ItemSet::new();
        for item in &item_set.0 {
            let next_step = item.selector.0.get(item.step_id as usize);
            if let Some(step) = next_step {
                if !step.is_immediate {
                    default_next_item_set.insert(*item);
                }
            }
        }
        self.output.states[state_id].default_next_state_id = self.add_state(default_next_item_set);

        self.item_set_list.push(item_set);
    }