Skip to main content

brokk_bifrost_python/graph/
hits.rs

1use crate::graph::extractor::ScanCtx;
2use brokk_bifrost_core::analyzer::Range;
3use brokk_bifrost_core::analyzer::usages::common::{
4    SNIPPET_CONTEXT_LINES, reclassify_self_receiver_hit_at, usage_hit,
5};
6use brokk_bifrost_core::text_utils::{find_line_index_for_offset, trimmed_snippet_around_line};
7use tree_sitter::Node;
8
9pub fn record_hit(node: Node<'_>, ctx: &mut ScanCtx<'_>) {
10    if let Some(hit) = build_hit(node, ctx) {
11        ctx.hits.insert(hit);
12    }
13}
14
15pub fn record_unproven_hit(node: Node<'_>, ctx: &mut ScanCtx<'_>) {
16    if let Some(hit) = build_hit(node, ctx) {
17        ctx.unproven_hits.insert(hit.into_unproven());
18    }
19}
20
21/// Record `node` as a same-owner self/cls receiver hit (#1014 facet B): a
22/// `self.member` / `cls.member` access whose receiver is the current instance /
23/// own class. Excluded from the external usage surface, counted as a same-owner
24/// site. Records the ordinary hit, then reclassifies it — the shared scan
25/// consumer, so the record ceremony lives in exactly one place.
26pub fn record_self_receiver_hit(node: Node<'_>, ctx: &mut ScanCtx<'_>) {
27    record_hit(node, ctx);
28    reclassify_self_receiver_hit_at(ctx.hits, ctx.file, node.start_byte(), node.end_byte());
29}
30
31/// Record `node` as an `Import`-binding hit (the token that brings the symbol
32/// into this file), which the IDE find-references surface includes but the
33/// call-graph surfaces ignore.
34pub fn record_import_hit(node: Node<'_>, ctx: &mut ScanCtx<'_>) {
35    if let Some(hit) = build_hit(node, ctx) {
36        ctx.hits.insert(hit.into_import());
37    }
38}
39
40fn build_hit(
41    node: Node<'_>,
42    ctx: &mut ScanCtx<'_>,
43) -> Option<brokk_bifrost_core::analyzer::usages::model::UsageHit> {
44    let start_byte = node.start_byte();
45    let end_byte = node.end_byte();
46    if start_byte >= end_byte {
47        return None;
48    }
49
50    let line_idx = find_line_index_for_offset(ctx.line_starts, start_byte);
51    let snippet =
52        trimmed_snippet_around_line(ctx.source, ctx.line_starts, line_idx, SNIPPET_CONTEXT_LINES);
53    let range = Range {
54        start_byte,
55        end_byte,
56        start_line: line_idx,
57        end_line: line_idx,
58    };
59
60    let enclosing = ctx.graph.index.enclosing_code_unit(ctx.file, &range)?;
61
62    Some(usage_hit(
63        ctx.file, line_idx, start_byte, end_byte, enclosing, snippet,
64    ))
65}