Skip to content

Dynamic context: enclosing function/class boundary detection #25

@haasonsaas

Description

@haasonsaas

Problem

DiffScope uses a fixed context window around changed lines. Qodo Merge's approach is smarter: search upward from each hunk for the enclosing function or class boundary, so the context always includes the full function signature and setup code. This dramatically improves the LLM's understanding of what the changed code does.

How Qodo Does It

From pr_agent/algo/git_patch_processing.py, function process_patch_lines():

  1. Calculate extended_start = max(1, start - patch_lines_before) (default 5 extra lines)
  2. Within those extra lines, search for section headers (function/class definitions)
  3. If found, reposition the hunk start to that header
  4. Asymmetric context: 5 lines before, 1 line after — preceding code matters more
  5. Validate context lines match between old and new file versions
  6. Configurable: max_extra_lines_before_dynamic_context = 10

Example: A change on line 42 inside a function that starts at line 35:

  • Fixed context (5 lines): shows lines 37-47 — misses function signature
  • Dynamic context: shows lines 35-43 — includes fn process_payment(order: &Order, amount: f64) -> Result<Receipt> which tells the LLM everything about the types and purpose

Proposed Solution

Enhance context gathering in context.rs / context_helpers.rs:

fn find_enclosing_boundary(
    file_content: &str,
    hunk_start: usize,
    max_search_lines: usize,  // default 10
) -> Option<usize> {
    // Search upward from hunk_start for function/class/method/impl boundaries
    // Use the same language-aware patterns from symbol_index.rs
    // Return the line number of the enclosing boundary
}

fn build_dynamic_context(
    file_content: &str,
    hunk: &DiffHunk,
    config: &ContextConfig,
) -> String {
    let boundary = find_enclosing_boundary(file_content, hunk.start_line, 10);
    let start = boundary.unwrap_or(hunk.start_line.saturating_sub(config.lines_before));
    let end = hunk.end_line + config.lines_after;  // asymmetric: fewer lines after
    extract_lines(file_content, start, end)
}

Configuration

context:
  dynamic: true  # default true
  max_search_lines_before: 10
  extra_lines_before: 5   # fallback if no boundary found
  extra_lines_after: 1    # asymmetric — less after

Architecture Fit

DiffScope already has:

  • Language-aware symbol patterns in symbol_index.rs (Rust, TypeScript, Python, Go, Java, etc.) ✅
  • Context fetcher (context.rs) ✅
  • Function chunker (function_chunker.rs) ✅

The symbol regex patterns can be reused to detect function/class boundaries during upward search.

Priority

Medium — high impact, low effort. This is a focused improvement to context quality that doesn't require architectural changes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions