fn handle_template_variable_matching()

in src/models/concrete_syntax.rs [225:317]


fn handle_template_variable_matching(
  cursor: &mut TreeCursor, source_code: &[u8], top_node: &Node, caps: regex::Captures,
  match_template: &str, one_plus: bool,
) -> (HashMap<String, CapturedNode>, Option<usize>) {
  let var_name = &caps["var_name"];
  let cs_adv_len = caps[0].len();
  let cs_advanced = ConcreteSyntax(
    match_template[cs_adv_len..]
      .to_string()
      .trim_start()
      .to_string(),
  );

  // Matching :[var] against a sequence of nodes [first_node, ... last_node]
  loop {
    let first_node = cursor.node();
    let mut last_node = first_node;

    // Determine whether a next node exists:
    let mut next_node_cursor = cursor.clone();
    let mut should_match = find_next_sibling_or_ancestor_sibling(&mut next_node_cursor);
    // At this point next_node_cursor either points to the first sibling of the first node,
    // or the first node itself, if such sibling no longer exists

    // Intentionally setting is_final_sibling to false regardless of should_match, due to the logic of handling the last iteration
    let mut is_final_sibling = false;
    loop {
      let mut tmp_cursor = next_node_cursor.clone();

      if let (mut recursive_matches, Some(last_matched_node_idx)) =
        get_matches_for_subsequence_of_nodes(
          &mut tmp_cursor,
          source_code,
          &cs_advanced,
          should_match,
          top_node,
        )
      {
        // Continuous code range that :[var] is matching from [first, ..., last]
        let matched_code = get_code_from_range(
          first_node.range().start_byte,
          last_node.range().end_byte,
          source_code,
        );

        // Check if :[var] was already matched against some code range
        // If it did, and it is not the same, we return unsuccessful
        if recursive_matches.contains_key(var_name)
          && recursive_matches[var_name].text.trim() != matched_code.trim()
        {
          return (HashMap::new(), None);
        }

        // Otherwise insert it
        recursive_matches.insert(
          var_name.to_string(),
          CapturedNode {
            range: Range::span_ranges(first_node.range(), last_node.range()),
            text: matched_code,
          },
        );
        return (recursive_matches, Some(last_matched_node_idx));
      }

      // Append an extra node to match with :[var]. Remember we had advanced next_node_cursor before,
      // therefore we cannot advance it again, otherwise we would skip nodes.
      // We only attempt to append an extra code if we are in one_plus matching mode.
      last_node = next_node_cursor.node();
      if is_final_sibling {
        break;
      }

      // This is used for the final iteration. We need to determine if there are any other nodes
      // left to match, to inform our next recursive call. We do this by calling find_next_sibling_or_ancestor_sibling
      // to move the cursor to the parent and find the next sibling at another level,
      // since at this level we already matched everything
      is_final_sibling = !next_node_cursor.goto_next_sibling();
      if is_final_sibling {
        should_match = find_next_sibling_or_ancestor_sibling(&mut next_node_cursor);
      }

      if !one_plus {
        break;
      }
    }

    // Move one level down, to attempt to match the template variable :[var] against smaller nodes.
    if !cursor.goto_first_child() {
      break;
    }
  }
  (HashMap::new(), None)
}