Skip to main content

brokk_bifrost_ruby/
adapter.rs

1//! The Ruby answers behind `RubyAdapter`.
2//!
3//! `LanguageAdapter` is analysis-owned, so the trait impl itself stays in
4//! `analyzer/ruby/adapter.rs`; every answer it gives comes from here or from
5//! [`crate::declarations`] and [`crate::test_detection`].
6
7use crate::declarations::{RubyVisitor, collect_ruby_identifiers};
8use brokk_bifrost_core::analyzer::ProjectFile;
9use brokk_bifrost_core::analyzer::cognitive_complexity;
10use brokk_bifrost_core::analyzer::parsed_file::ParsedFile;
11use std::sync::LazyLock;
12use tree_sitter::Tree;
13
14pub const RUBY_FILE_EXTENSION: &str = "rb";
15
16/// Tree-sitter node-kind mapping used by the cognitive-complexity scorer for
17/// Ruby. Node names are from the tree-sitter-ruby grammar.
18pub static RUBY_COGNITIVE_CONFIG: LazyLock<cognitive_complexity::Config> =
19    LazyLock::new(|| cognitive_complexity::Config {
20        if_types: &["if", "unless", "if_modifier", "unless_modifier"],
21        alternate_if_types: &["elsif"],
22        loop_types: &["while", "until", "for", "while_modifier", "until_modifier"],
23        catch_types: &["rescue"],
24        conditional_types: &["conditional"],
25        case_types: &["when", "in_clause"],
26        binary_types: &["binary"],
27        logical_operators: &["&&", "||", "and", "or"],
28        named_function_boundary_types: &["method", "singleton_method"],
29        anonymous_function_types: &["block", "do_block", "lambda"],
30        ..cognitive_complexity::Config::empty()
31    });
32
33pub fn ruby_extract_call_receiver(reference: &str) -> Option<String> {
34    let trimmed = reference.trim();
35    let before_args = trimmed
36        .split_once('(')
37        .map(|(head, _)| head)
38        .unwrap_or(trimmed);
39    // Ruby receivers are separated by `.` (method) or `::` (namespace).
40    if let Some((receiver, _)) = before_args.rsplit_once("::") {
41        return Some(receiver.to_string());
42    }
43    before_args
44        .rsplit_once('.')
45        .map(|(receiver, _)| receiver.to_string())
46}
47
48pub fn parse_ruby_file(file: &ProjectFile, source: &str, tree: &Tree) -> ParsedFile {
49    let mut parsed = ParsedFile::new(String::new());
50    let root = tree.root_node();
51
52    collect_ruby_identifiers(root, source, &mut parsed.type_identifiers);
53
54    let mut visitor = RubyVisitor {
55        file,
56        source,
57        parsed: &mut parsed,
58    };
59    visitor.visit_program(root);
60
61    parsed
62}