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::path::Path;
use std::sync::Arc;

use rayon::prelude::*;
use rustc_hash::{FxHashMap, FxHashSet};

use crate::config::extensions::CODE_EXTENSIONS;
use crate::config::filtering::FILTERING;
use crate::graph::{EdgeCategory, Graph};
use crate::types::{DiffHunk, Fragment, FragmentId, FragmentKind};

fn fragment_hunk_gap(frag_start: u32, frag_end: u32, hunk_start: u32, hunk_end: u32) -> u32 {
    if frag_end < hunk_start {
        hunk_start - frag_end
    } else if frag_start > hunk_end {
        frag_start - hunk_end
    } else {
        0
    }
}

fn proximity_score(frag: &Fragment, file_hunks: &[(u32, u32)]) -> f64 {
    let min_gap = file_hunks
        .iter()
        .map(|&(h_start, h_end)| {
            fragment_hunk_gap(frag.start_line(), frag.end_line(), h_start, h_end)
        })
        .min()
        .unwrap_or(u32::MAX);
    let half_decay = if frag.kind == FragmentKind::Definition {
        FILTERING.definition_proximity_half_decay
    } else {
        FILTERING.proximity_half_decay
    };
    FILTERING.proximity_floor_max / (1.0 + min_gap as f64 / half_decay)
}

fn effective_relevance_threshold(token_count: u32) -> f64 {
    let size_factor = (token_count as f64 / FILTERING.size_penalty_base_tokens)
        .max(1.0)
        .powf(FILTERING.size_penalty_exponent);
    FILTERING.low_relevance_threshold * size_factor
}

pub fn apply_hunk_proximity_bonus(
    rel: &mut FxHashMap<FragmentId, f64>,
    core_ids: &FxHashSet<FragmentId>,
    fragments: &[Fragment],
    hunks: &[DiffHunk],
) {
    let mut hunks_by_path: FxHashMap<&str, Vec<(u32, u32)>> = FxHashMap::default();
    for h in hunks {
        let (h_start, h_end) = h.core_selection_range();
        hunks_by_path
            .entry(h.path.as_ref())
            .or_default()
            .push((h_start, h_end));
    }

    let bonuses: Vec<(FragmentId, f64)> = fragments
        .par_iter()
        .filter(|frag| !core_ids.contains(&frag.id))
        .filter_map(|frag| {
            let file_hunks = hunks_by_path.get(frag.path())?;
            let bonus = proximity_score(frag, file_hunks);
            Some((frag.id.clone(), bonus))
        })
        .collect();

    for (id, bonus) in bonuses {
        let current = rel.get(&id).copied().unwrap_or(0.0);
        if current < bonus {
            rel.insert(id, bonus);
        }
    }
}

fn classify_semantic_edges(
    graph: &Graph,
    changed_paths: &FxHashSet<Arc<str>>,
) -> (
    FxHashMap<Arc<str>, FxHashSet<Arc<str>>>,
    FxHashSet<Arc<str>>,
) {
    let mut reverse_deps: FxHashMap<Arc<str>, FxHashSet<Arc<str>>> = FxHashMap::default();
    let mut direct_edge_paths: FxHashSet<Arc<str>> = FxHashSet::default();

    graph.for_each_categorized_edge(|src, dst, category| {
        if category != EdgeCategory::Semantic {
            return;
        }
        let src_changed = changed_paths.contains(&src.path);
        let dst_changed = changed_paths.contains(&dst.path);
        if !(src_changed ^ dst_changed) {
            return;
        }

        let (changed_frag, other_frag) = if src_changed { (src, dst) } else { (dst, src) };

        let fwd_w = graph
            .forward_edge_weight(changed_frag, other_frag)
            .unwrap_or(0.0);
        let rev_w = graph
            .forward_edge_weight(other_frag, changed_frag)
            .unwrap_or(0.0);

        if rev_w > fwd_w {
            reverse_deps
                .entry(changed_frag.path.clone())
                .or_default()
                .insert(other_frag.path.clone());
        } else {
            direct_edge_paths.insert(other_frag.path.clone());
        }
    });

    (reverse_deps, direct_edge_paths)
}

fn find_hub_noise_paths(graph: &Graph, changed_paths: &FxHashSet<Arc<str>>) -> FxHashSet<Arc<str>> {
    let (reverse_deps, direct_edge_paths) = classify_semantic_edges(graph, changed_paths);

    let changed_dirs: FxHashSet<String> = changed_paths
        .iter()
        .filter_map(|p| {
            Path::new(p.as_ref())
                .parent()
                .map(|d| d.to_string_lossy().into_owned())
        })
        .collect();

    let mut noise_counts: FxHashMap<Arc<str>, usize> = FxHashMap::default();
    for (hub_path, deps) in &reverse_deps {
        if changed_paths.contains(hub_path) {
            continue;
        }
        if deps.len() >= FILTERING.hub_reverse_threshold {
            for dep in deps {
                *noise_counts.entry(dep.clone()).or_insert(0) += 1;
            }
        }
    }

    noise_counts
        .into_iter()
        .filter(|(p, _count)| {
            !direct_edge_paths.contains(p)
                && !changed_dirs.contains(
                    &Path::new(p.as_ref())
                        .parent()
                        .map(|d| d.to_string_lossy().into_owned())
                        .unwrap_or_default(),
                )
        })
        .map(|(p, _)| p)
        .collect()
}

fn find_config_generic_code_files(
    graph: &Graph,
    changed_paths: &FxHashSet<Arc<str>>,
) -> FxHashSet<Arc<str>> {
    let mut has_real_edge: FxHashSet<Arc<str>> = FxHashSet::default();
    let mut has_generic_config: FxHashSet<Arc<str>> = FxHashSet::default();
    let mut generic_edge_count: FxHashMap<Arc<str>, usize> = FxHashMap::default();
    let config_stems: FxHashSet<String> = changed_paths
        .iter()
        .filter_map(|p| {
            Path::new(p.as_ref())
                .file_stem()
                .map(|s| s.to_string_lossy().to_lowercase())
        })
        .collect();

    graph.for_each_categorized_edge(|src, dst, category| {
        let src_changed = changed_paths.contains(&src.path);
        let dst_changed = changed_paths.contains(&dst.path);
        if !(src_changed ^ dst_changed) {
            return;
        }
        let other_path = if src_changed { &dst.path } else { &src.path };
        match category {
            EdgeCategory::ConfigGeneric => {
                has_generic_config.insert(other_path.clone());
                *generic_edge_count.entry(other_path.clone()).or_insert(0) += 1;
            }
            EdgeCategory::Semantic | EdgeCategory::Config => {
                has_real_edge.insert(other_path.clone());
            }
            _ => {}
        }
    });

    let generic_only: FxHashSet<Arc<str>> = has_generic_config
        .difference(&has_real_edge)
        .cloned()
        .collect();

    generic_only
        .into_iter()
        .filter(|p| {
            let path = Path::new(p.as_ref());
            let ext = path
                .extension()
                .map(|e| format!(".{}", e.to_string_lossy().to_lowercase()))
                .unwrap_or_default();
            let stem = path
                .file_stem()
                .map(|s| s.to_string_lossy().to_lowercase())
                .unwrap_or_default();
            CODE_EXTENSIONS.contains(ext.as_str())
                && generic_edge_count.get(p).copied().unwrap_or(0) <= 1
                && !config_stems.contains(&stem)
        })
        .collect()
}

pub fn filter_unrelated_fragments(
    fragments: &[Fragment],
    core_ids: &FxHashSet<FragmentId>,
    graph: &Graph,
) -> Vec<Fragment> {
    let changed_paths: FxHashSet<Arc<str>> = core_ids.iter().map(|fid| fid.path.clone()).collect();

    let mut paths_to_remove = find_hub_noise_paths(graph, &changed_paths);
    let config_generic = find_config_generic_code_files(graph, &changed_paths);
    for p in config_generic {
        paths_to_remove.insert(p);
    }
    for p in &changed_paths {
        paths_to_remove.remove(p);
    }

    fragments
        .iter()
        .filter(|f| !paths_to_remove.contains(&f.id.path))
        .cloned()
        .collect()
}

pub fn filter_low_relevance(
    fragments: Vec<Fragment>,
    core_ids: &FxHashSet<FragmentId>,
    rel: &FxHashMap<FragmentId, f64>,
) -> Vec<Fragment> {
    fragments
        .into_iter()
        .filter(|f| {
            core_ids.contains(&f.id)
                || rel.get(&f.id).copied().unwrap_or(0.0)
                    >= effective_relevance_threshold(f.token_count)
        })
        .collect()
}

pub fn filter_positive_relevance(
    fragments: Vec<Fragment>,
    core_ids: &FxHashSet<FragmentId>,
    rel: &FxHashMap<FragmentId, f64>,
) -> Vec<Fragment> {
    fragments
        .into_iter()
        .filter(|f| core_ids.contains(&f.id) || rel.get(&f.id).copied().unwrap_or(0.0) > 0.0)
        .collect()
}

pub fn cap_context_fragments(
    fragments: Vec<Fragment>,
    core_ids: &FxHashSet<FragmentId>,
    rel: &FxHashMap<FragmentId, f64>,
) -> Vec<Fragment> {
    let changed_paths: FxHashSet<Arc<str>> = core_ids.iter().map(|fid| fid.path.clone()).collect();

    let mut ctx_by_path: FxHashMap<Arc<str>, Vec<Fragment>> = FxHashMap::default();
    let mut result: Vec<Fragment> = Vec::new();

    for f in fragments {
        if changed_paths.contains(&f.id.path) {
            result.push(f);
        } else {
            ctx_by_path.entry(f.id.path.clone()).or_default().push(f);
        }
    }

    for (_path, mut file_frags) in ctx_by_path {
        if file_frags.len() <= FILTERING.max_context_fragments_per_file {
            result.extend(file_frags);
        } else {
            file_frags.sort_by(|a, b| {
                let sa = rel.get(&a.id).copied().unwrap_or(0.0);
                let sb = rel.get(&b.id).copied().unwrap_or(0.0);
                sb.total_cmp(&sa).then_with(|| a.id.cmp(&b.id))
            });
            result.extend(
                file_frags
                    .into_iter()
                    .take(FILTERING.max_context_fragments_per_file),
            );
        }
    }

    result.sort_by(|a, b| a.id.cmp(&b.id));
    result
}