fn try_match_node_range()

in crates/concrete-syntax/src/models/concrete_syntax/interpreter.rs [359:435]


fn try_match_node_range(
  ctx: &mut MatchingContext<'_>, var_name: &str, constraints: &[CsConstraint],
  remaining_pattern: &[ResolvedCsElement], allow_horizontal_expansion: bool,
) -> PatternMatchResult {
  // 1. Initial anchors
  let range_start = ctx.cursor.node();
  let mut range_end = range_start.clone();
  let mut next_cursor = ctx.cursor.clone();
  // 'should_match' is used to check whether there are siblings or ancestor siblings we need to match
  // after we assign the current range to the capture node
  let mut should_match = CursorNavigator::find_next_sibling_or_ancestor_sibling(&mut next_cursor);
  let mut is_last = false;

  // Helper to slice out the captured text
  let make_capture = |end: &Node| {
    let node_type = if range_start.range() == end.range() {
      range_start.kind().to_string() // Single node - use actual type
    } else {
      "multi_node".to_string() // Multiple nodes - use special type
    };

    CapturedNode {
      range: Range::span_ranges(range_start.range(), end.range()),
      text: CursorNavigator::get_text_from_range(
        range_start.range().start_byte,
        end.range().end_byte,
        ctx.source_code,
      ),
      node_type,
    }
  };

  loop {
    // --- 1) try current [start...end] slice ---
    let captured = make_capture(&range_end);
    let constraint_context = ConstraintContext {
      captured_node: &captured,
      source_code: ctx.source_code,
      ast_root: &ctx.cursor.node(),
    };
    if satisfies_constraints(&captured, constraints, Some(&constraint_context)) {
      let mut sub_ctx = MatchingContext {
        cursor: next_cursor.clone(),
        source_code: ctx.source_code,
        top_node: ctx.top_node,
      };
      if let PatternMatchResult::Success {
        mut captures,
        consumed_nodes,
        range: _,
      } = match_cs_pattern(&mut sub_ctx, remaining_pattern, should_match)
      {
        // conflict check
        if let Some(prev) = captures.get(var_name) {
          if prev.text.trim() != captured.text.trim() {
            return PatternMatchResult::failed();
          }
        }
        captures.insert(var_name.to_owned(), captured);
        return PatternMatchResult::success(captures, consumed_nodes);
      }
    }

    // --- 2) need to expand slice? ---
    if !allow_horizontal_expansion || is_last {
      return PatternMatchResult::failed();
    }

    // grow the range to include the node under next_cursor
    range_end = next_cursor.node();
    is_last = !next_cursor.goto_next_sibling();
    if is_last {
      // once we're at the end of the range, check if there is any ancestor left to match
      should_match = CursorNavigator::find_next_sibling_or_ancestor_sibling(&mut next_cursor);
    }
  }
}