diffctx 1.12.1

Selects the minimum code an LLM needs to review a git diff: walks the dependency graph outward from changed lines and stops when extra context stops paying for itself
Documentation
use std::sync::Arc;

use rustc_hash::FxHashSet;

use crate::config::graph_filtering::GRAPH_FILTERING;
use crate::types::{Fragment, FragmentId, FragmentKind};

fn is_signature_eligible(kind: FragmentKind) -> bool {
    // Variable covers TS/JS arrow-function bindings (`const f = (...) => {...}`);
    // without a stub variant a large changed arrow function that misses the core
    // budget vanishes from the output entirely (#106).
    matches!(
        kind,
        FragmentKind::Function
            | FragmentKind::Class
            | FragmentKind::Struct
            | FragmentKind::Interface
            | FragmentKind::Enum
            | FragmentKind::Variable
    )
}

fn signature_kind(kind: FragmentKind) -> FragmentKind {
    match kind {
        FragmentKind::Function => FragmentKind::FunctionSignature,
        FragmentKind::Class => FragmentKind::ClassSignature,
        FragmentKind::Struct => FragmentKind::StructSignature,
        FragmentKind::Interface => FragmentKind::InterfaceSignature,
        FragmentKind::Enum => FragmentKind::EnumSignature,
        _ => FragmentKind::FunctionSignature,
    }
}

fn count_brackets_outside_strings(line: &str) -> (i32, i32, i32, i32) {
    let mut open_parens = 0i32;
    let mut close_parens = 0i32;
    let mut open_braces = 0i32;
    let mut close_braces = 0i32;
    let mut in_string: Option<char> = None;
    let mut escaped = false;

    for ch in line.chars() {
        if let Some(quote) = in_string {
            if escaped {
                escaped = false;
            } else if ch == '\\' {
                escaped = true;
            } else if ch == quote {
                in_string = None;
            }
            continue;
        }
        match ch {
            '\'' | '"' | '`' => {
                in_string = Some(ch);
                escaped = false;
            }
            '(' => open_parens += 1,
            ')' => close_parens += 1,
            '{' => open_braces += 1,
            '}' => close_braces += 1,
            _ => {}
        }
    }

    (open_parens, close_parens, open_braces, close_braces)
}

fn decorator_prefix_len(lines: &[&str]) -> usize {
    let mut i = 0;
    let mut paren_depth = 0i32;
    while i < lines.len() {
        let trimmed = lines[i].trim_start();
        let starts_decorator = trimmed.starts_with('@') || trimmed.starts_with("#[");
        if paren_depth <= 0 && !starts_decorator {
            break;
        }
        let (op, cp, _, _) = count_brackets_outside_strings(lines[i]);
        paren_depth += op - cp;
        i += 1;
    }
    if i >= lines.len() { 0 } else { i }
}

fn find_signature_end(lines: &[&str]) -> usize {
    let mut paren_depth = 0i32;
    let mut seen_open_paren = false;

    for (i, line) in lines.iter().enumerate() {
        let (op, cp, ob, cb) = count_brackets_outside_strings(line);
        paren_depth += op - cp;
        if op > 0 {
            seen_open_paren = true;
        }
        // A body-opening brace only ends the signature once we are outside the
        // parameter list. Braces inside parameter defaults or annotations
        // (e.g. Python `def f(x={}):`) appear while `paren_depth > 0` and must
        // not truncate the signature mid-parameter-list.
        if paren_depth <= 0 && ob - cb > 0 {
            return i + 1;
        }
        if seen_open_paren && paren_depth <= 0 {
            return i + 1;
        }
    }

    2.min(lines.len())
}

pub fn generate_signature_variants(fragments: &[Fragment]) -> Vec<Fragment> {
    let mut signatures: Vec<Fragment> = Vec::new();
    let mut seen: FxHashSet<FragmentId> = FxHashSet::default();

    for frag in fragments {
        if !is_signature_eligible(frag.kind) {
            continue;
        }
        if frag.line_count() < GRAPH_FILTERING.min_lines_for_signature {
            continue;
        }
        let lines: Vec<&str> = frag.content.lines().collect();
        let decorators = decorator_prefix_len(&lines);
        let sig_end = decorators + find_signature_end(&lines[decorators..]);
        let sig_content: String = lines[..sig_end].join("\n");
        let sig_end_line = frag.start_line() + sig_end as u32 - 1;
        let sig_id = FragmentId::new(frag.id.path.clone(), frag.start_line(), sig_end_line);

        if seen.contains(&sig_id) {
            continue;
        }
        seen.insert(sig_id.clone());

        signatures.push(Fragment {
            id: sig_id,
            kind: signature_kind(frag.kind),
            content: Arc::from(sig_content),
            identifiers: frag.identifiers.clone(),
            token_count: 0,
            symbol_name: frag.symbol_name.clone(),
        });
    }

    signatures
}