Liking cljdoc? Tell your friends :D

Hierarchical Scope Completion Analysis

This directory contains the design analysis and benchmark results for the hierarchical lexical scope filtering feature.

Overview

Feature: Contextual code completion with hierarchical lexical scopes Timeline: October 2025 Final Result: Official production feature with 4.7% improvement over baseline Status: Complete - 233 tests passing

Purpose

Hierarchical scope completion enables contextual code completion that respects lexical scoping rules. When a user types a partial identifier, the completion system only suggests identifiers visible from the current scope.

Use Cases

  • IDE Code Completion: Only show variables/functions visible in current context
  • REPL Environments: Filter completions by namespace/module visibility
  • Configuration Editors: Scope-aware key completion
  • Query Languages: Context-aware field/function suggestions

Documents

DESIGN.md

Design Document - Comprehensive Approach Analysis

  • Approaches Evaluated: 6 different implementations analyzed
  • Complexity Analysis: Time/space trade-offs for each approach
  • Benchmark Plan: 4 test scenarios (shallow, deep, wide, dense hierarchies)
  • Theoretical Foundation: O(n+m) intersection algorithms
  • Final Recommendations: Sorted Vector and Bitmask approaches

Key Findings:

  1. Sorted Vector: 4.7% faster than HashSet (general purpose)
  2. Bitmask: 7.9% faster than HashSet (≤64 scopes)
  3. Hybrid approaches have enum matching overhead
  4. Post-filtering ≈ early filtering (within 0.05%)

RESULTS.md

Benchmark Results - Production Validation

  • Benchmarks Run: 5 approaches across 4 scenarios (10,000 terms each)
  • Winner: Sorted Vector (Vec) for general-purpose use
  • Fastest (when applicable): Bitmask (u64) for ≤64 scopes
  • Performance Metrics: 386-420μs per query across all scenarios
  • Memory Comparison: Detailed per-term overhead analysis

Performance Results:

ApproachAverage Timevs BaselineRecommendation
Sorted Vector400.2μs-4.7%⭐ Primary choice
Bitmask386.7μs-7.9%Fast for ≤64 scopes
Hybrid409.3μs-2.5%Not recommended
HashSet419.8μsbaselineReference

Implementation Files

Helper Functions

Location: src/transducer/helpers.rs

Provides two optimized intersection implementations:

  1. sorted_vec_intersection() - General-purpose O(n+m) two-pointer scan
  2. bitmask_intersection() - O(1) bitwise AND for ≤64 scopes

Tests: 9 comprehensive tests (all passing)

Working Example

Location: examples/hierarchical_scope_completion.rs

Demonstrates:

  • Scope-aware code completion engine
  • Multiple test scenarios
  • Integration with transducer API
  • Performance characteristics

Benchmarks

Location: benches/hierarchical_scope_benchmarks.rs

  • 540+ lines of comprehensive testing
  • 5 approaches benchmarked
  • 4 scenarios: shallow, deep, wide, dense
  • Criterion-based with statistical analysis

User Guide

For integration instructions and usage examples, see:

Hierarchical Scope Completion Guide

Topics covered:

  • Quick start with code examples
  • API reference and best practices
  • Performance tips and scalability
  • Troubleshooting common issues

Key Insights

1. Cache Locality Matters

Sorted vectors (contiguous memory) outperform hash tables due to better cache behavior, even with O(n+m) vs O(k) complexity.

2. Early Filtering Provides Minimal Benefit

Post-filtering is within 0.05% of early filtering, showing that graph traversal dominates query time.

3. Enum Overhead Is Significant

Hybrid approaches with enum variants underperform due to branch misprediction and matching overhead.

4. Bitmask Optimization Works Excellently

For ≤64 scopes, bitmask approach provides 7.9% improvement with minimal memory (8 bytes per term).

5. Two-Pointer Scan Is Highly Optimized

CPU-level optimizations for sequential scans make sorted vector intersection very fast despite O(n+m) complexity.

Theoretical Foundation

Based on "Fast String Correction with Levenshtein-Automata" by Schulz & Mihov (2002):

  • O(n) deterministic Levenshtein automata construction
  • Extensions for transpositions, merges, and splits
  • Combined with efficient set intersection for scope filtering

Test Scenarios

Scenario 1: Shallow Hierarchy (10 scopes)

  • Terms: 10,000 identifiers
  • Avg scopes/term: 3
  • Typical: Local variables in functions
  • Best: Bitmask (390μs, 6.2% faster)

Scenario 2: Deep Hierarchy (100 scopes)

  • Terms: 10,000 identifiers
  • Avg scopes/term: 8
  • Typical: Nested classes/modules
  • Best: Sorted Vec (399μs, 4.9% faster)

Scenario 3: Wide Hierarchy (1000 scopes)

  • Terms: 10,000 identifiers
  • Avg scopes/term: 2
  • Typical: Large flat namespace
  • Best: Sorted Vec (417μs, 0.5% faster)

Scenario 4: Dense Graph (50 scopes, 15/term)

  • Terms: 10,000 identifiers
  • Avg scopes/term: 15
  • Typical: Inherited/imported symbols
  • Best: Sorted Vec (389μs, 8.9% faster)

API Integration

Seamlessly integrates with existing query_filtered() API:

use liblevenshtein::prelude::*;
use liblevenshtein::transducer::helpers::sorted_vec_intersection;

let dict = PathMapDictionary::from_terms_with_values(terms);
let transducer = Transducer::new(dict, Algorithm::Standard);

let visible_scopes = vec![0, 1, 2];
let results: Vec<_> = transducer
    .query_filtered("var", 2, |term_scopes| {
        sorted_vec_intersection(term_scopes, &visible_scopes)
    })
    .map(|c| c.term)
    .collect();

Production Status

Production-Ready:

  • All 233 tests passing (154 unit + 79 integration/property tests)
  • Comprehensive benchmarks validating performance
  • Well-documented API with examples
  • Helper functions thoroughly tested

Future Enhancements

Potential optimizations (not currently needed):

  1. True Pruning: Store scope metadata on trie nodes

    • Estimated 10-30% improvement
    • Requires breaking change to dictionary traits
  2. SIMD Acceleration: Vectorize array intersection

    • Potential 2-3x speedup
    • Requires unstable Rust features
  3. Adaptive Strategies: Runtime selection based on scope count

    • Bitmask for ≤64, sorted vec for >64
    • Zero-cost abstraction via generics

Related Documentation

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close