Skip to main content

brokk_bifrost_python/
adapter.rs

1//! The Python answers behind `PythonAdapter`.
2//!
3//! `LanguageAdapter` is analysis-owned, so the trait impl itself stays in
4//! `analyzer/python/adapter.rs`; every answer it gives comes from here or from
5//! [`crate::declarations`]. `synthesize_hydrated_units` is the one exception:
6//! it mutates `FileState`, an analysis type, and stays with the shell.
7
8use brokk_bifrost_core::analyzer::cognitive_complexity;
9use std::sync::LazyLock;
10
11use crate::declarations::python_is_decorated_function_boundary;
12
13pub const PYTHON_FILE_EXTENSION: &str = "py";
14
15/// Tree-sitter node-kind mapping used by the cognitive-complexity scorer
16/// for Python. Mirrors `ai.brokk.analyzer.python.CognitiveComplexityAnalysis`.
17pub static PYTHON_COGNITIVE_CONFIG: LazyLock<cognitive_complexity::Config> =
18    LazyLock::new(|| cognitive_complexity::Config {
19        if_types: &["if_statement"],
20        alternate_if_types: &["elif_clause"],
21        loop_types: &["for_statement", "while_statement"],
22        catch_types: &["except_clause"],
23        conditional_types: &["conditional_expression"],
24        case_types: &["case_clause"],
25        binary_types: &["boolean_operator"],
26        logical_operators: &["and", "or"],
27        named_function_boundary_types: &["function_definition"],
28        anonymous_function_types: &["lambda"],
29        named_function_boundary_predicate: Some(python_is_decorated_function_boundary),
30        ..cognitive_complexity::Config::empty()
31    });
32
33pub fn python_extract_call_receiver(reference: &str) -> Option<String> {
34    brokk_bifrost_core::analyzer::common::extract_call_reference_parts(reference, &["."])
35        .map(|(receiver, _)| receiver.to_string())
36}
37
38/// Literal string data may occupy a long physical line, but replacement fields
39/// remain executable structure. Bytes and implicit concatenation use the same
40/// grammar nodes. The caller validates the finite source/tree envelope first.
41pub fn is_string_line_data(
42    node: tree_sitter::Node<'_>,
43    should_stop: &mut dyn FnMut() -> bool,
44) -> std::ops::ControlFlow<(), bool> {
45    use brokk_bifrost_core::analyzer::tree_walk::{WalkControl, walk_tree_preorder};
46    use std::ops::ControlFlow;
47    if !matches!(node.kind(), "string" | "concatenated_string") {
48        return ControlFlow::Continue(false);
49    }
50    let mut result = ControlFlow::Continue(true);
51    walk_tree_preorder(node, true, |current| {
52        if should_stop() {
53            result = ControlFlow::Break(());
54            WalkControl::Break
55        } else if current.is_error() || current.is_missing() || current.kind() == "interpolation" {
56            result = ControlFlow::Continue(false);
57            WalkControl::Break
58        } else {
59            WalkControl::Continue
60        }
61    });
62    result
63}