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)
}